rippled
BookStep.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/FlatSets.h>
22 #include <ripple/app/paths/impl/Steps.h>
23 #include <ripple/app/tx/impl/OfferStream.h>
24 #include <ripple/basics/IOUAmount.h>
25 #include <ripple/basics/Log.h>
26 #include <ripple/basics/XRPAmount.h>
27 #include <ripple/basics/contract.h>
28 #include <ripple/ledger/Directory.h>
29 #include <ripple/ledger/PaymentSandbox.h>
30 #include <ripple/protocol/Book.h>
31 #include <ripple/protocol/Feature.h>
32 #include <ripple/protocol/Quality.h>
33 
34 #include <boost/container/flat_set.hpp>
35 
36 #include <numeric>
37 #include <sstream>
38 
39 namespace ripple {
40 
41 template <class TIn, class TOut, class TDerived>
42 class BookStep : public StepImp<TIn, TOut, BookStep<TIn, TOut, TDerived>>
43 {
44 protected:
45  uint32_t const maxOffersToConsume_;
49  // Charge transfer fees when the prev step redeems
50  Step const* const prevStep_ = nullptr;
52  // Mark as inactive (dry) if too many offers are consumed
53  bool inactive_ = false;
63 
64  struct Cache
65  {
66  TIn in;
67  TOut out;
68 
69  Cache(TIn const& in_, TOut const& out_) : in(in_), out(out_)
70  {
71  }
72  };
73 
75 
76  static uint32_t
78  {
79  if (ctx.view.rules().enabled(fix1515))
80  return 1000;
81  return 2000;
82  }
83 
84 public:
85  BookStep(StrandContext const& ctx, Issue const& in, Issue const& out)
87  , book_(in, out)
88  , strandSrc_(ctx.strandSrc)
89  , strandDst_(ctx.strandDst)
90  , prevStep_(ctx.prevStep)
91  , ownerPaysTransferFee_(ctx.ownerPaysTransferFee)
92  , j_(ctx.j)
93  {
94  }
95 
96  Book const&
97  book() const
98  {
99  return book_;
100  }
101 
103  cachedIn() const override
104  {
105  if (!cache_)
106  return std::nullopt;
107  return EitherAmount(cache_->in);
108  }
109 
111  cachedOut() const override
112  {
113  if (!cache_)
114  return std::nullopt;
115  return EitherAmount(cache_->out);
116  }
117 
119  debtDirection(ReadView const& sb, StrandDirection dir) const override
120  {
123  }
124 
126  bookStepBook() const override
127  {
128  return book_;
129  }
130 
132  qualityUpperBound(ReadView const& v, DebtDirection prevStepDir)
133  const override;
134 
136  offersUsed() const override;
137 
139  revImp(
140  PaymentSandbox& sb,
141  ApplyView& afView,
142  boost::container::flat_set<uint256>& ofrsToRm,
143  TOut const& out);
144 
146  fwdImp(
147  PaymentSandbox& sb,
148  ApplyView& afView,
149  boost::container::flat_set<uint256>& ofrsToRm,
150  TIn const& in);
151 
153  validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in)
154  override;
155 
156  // Check for errors frozen constraints.
157  TER
158  check(StrandContext const& ctx) const;
159 
160  bool
161  inactive() const override
162  {
163  return inactive_;
164  }
165 
166 protected:
168  logStringImpl(char const* name) const
169  {
170  std::ostringstream ostr;
171  ostr << name << ": "
172  << "\ninIss: " << book_.in.account
173  << "\noutIss: " << book_.out.account
174  << "\ninCur: " << book_.in.currency
175  << "\noutCur: " << book_.out.currency;
176  return ostr.str();
177  }
178 
179 private:
180  friend bool
181  operator==(BookStep const& lhs, BookStep const& rhs)
182  {
183  return lhs.book_ == rhs.book_;
184  }
185 
186  friend bool
187  operator!=(BookStep const& lhs, BookStep const& rhs)
188  {
189  return !(lhs == rhs);
190  }
191 
192  bool
193  equal(Step const& rhs) const override;
194 
195  // Iterate through the offers at the best quality in a book.
196  // Unfunded offers and bad offers are skipped (and returned).
197  // callback is called with the offer SLE, taker pays, taker gets.
198  // If callback returns false, don't process any more offers.
199  // Return the unfunded and bad offers and the number of offers consumed.
200  template <class Callback>
202  forEachOffer(
203  PaymentSandbox& sb,
204  ApplyView& afView,
205  DebtDirection prevStepDebtDir,
206  Callback& callback) const;
207 
208  void
209  consumeOffer(
210  PaymentSandbox& sb,
211  TOffer<TIn, TOut>& offer,
212  TAmounts<TIn, TOut> const& ofrAmt,
213  TAmounts<TIn, TOut> const& stepAmt,
214  TOut const& ownerGives) const;
215 };
216 
217 //------------------------------------------------------------------------------
218 
219 // Flow is used in two different circumstances for transferring funds:
220 // o Payments, and
221 // o Offer crossing.
222 // The rules for handling funds in these two cases are almost, but not
223 // quite, the same.
224 
225 // Payment BookStep template class (not offer crossing).
226 template <class TIn, class TOut>
227 class BookPaymentStep : public BookStep<TIn, TOut, BookPaymentStep<TIn, TOut>>
228 {
229 public:
230  explicit BookPaymentStep() = default;
231 
234 
235  // Never limit self cross quality on a payment.
236  bool
238  AccountID const&,
239  AccountID const&,
240  TOffer<TIn, TOut> const& offer,
243  bool) const
244  {
245  return false;
246  }
247 
248  // A payment can look at offers of any quality
249  bool
251  {
252  return true;
253  }
254 
255  // For a payment ofrInRate is always the same as trIn.
258  const
259  {
260  return trIn;
261  }
262 
263  // For a payment ofrOutRate is always the same as trOut.
266  Step const*,
267  TOffer<TIn, TOut> const&,
268  AccountID const&,
269  std::uint32_t trOut) const
270  {
271  return trOut;
272  }
273 
274  Quality
276  ReadView const& v,
277  Quality const& ofrQ,
278  DebtDirection prevStepDir) const
279  {
280  // Charge the offer owner, not the sender
281  // Charge a fee even if the owner is the same as the issuer
282  // (the old code does not charge a fee)
283  // Calculate amount that goes to the taker and the amount charged the
284  // offer owner
285  auto rate = [&](AccountID const& id) {
286  if (isXRP(id) || id == this->strandDst_)
287  return parityRate;
288  return transferRate(v, id);
289  };
290 
291  auto const trIn =
292  redeems(prevStepDir) ? rate(this->book_.in.account) : parityRate;
293  // Always charge the transfer fee, even if the owner is the issuer
294  auto const trOut = this->ownerPaysTransferFee_
295  ? rate(this->book_.out.account)
296  : parityRate;
297 
298  Quality const q1{getRate(STAmount(trOut.value), STAmount(trIn.value))};
299  return composed_quality(q1, ofrQ);
300  }
301 
303  logString() const override
304  {
305  return this->logStringImpl("BookPaymentStep");
306  }
307 };
308 
309 // Offer crossing BookStep template class (not a payment).
310 template <class TIn, class TOut>
312  : public BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>
313 {
316 
317 private:
318  // Helper function that throws if the optional passed to the constructor
319  // is none.
320  static Quality
321  getQuality(std::optional<Quality> const& limitQuality)
322  {
323  // It's really a programming error if the quality is missing.
324  assert(limitQuality);
325  if (!limitQuality)
326  Throw<FlowException>(tefINTERNAL, "Offer requires quality.");
327  return *limitQuality;
328  }
329 
330 public:
332  StrandContext const& ctx,
333  Issue const& in,
334  Issue const& out)
335  : BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>(ctx, in, out)
337  , qualityThreshold_(getQuality(ctx.limitQuality))
338  {
339  }
340 
341  bool
343  AccountID const& strandSrc,
344  AccountID const& strandDst,
345  TOffer<TIn, TOut> const& offer,
348  bool const offerAttempted) const
349  {
350  // This method supports some correct but slightly surprising
351  // behavior in offer crossing. The scenario:
352  //
353  // o alice has already created one or more offers.
354  // o alice creates another offer that can be directly crossed (not
355  // autobridged) by one or more of her previously created offer(s).
356  //
357  // What does the offer crossing do?
358  //
359  // o The offer crossing could go ahead and cross the offers leaving
360  // either one reduced offer (partial crossing) or zero offers
361  // (exact crossing) in the ledger. We don't do this. And, really,
362  // the offer creator probably didn't want us to.
363  //
364  // o We could skip over the self offer in the book and only cross
365  // offers that are not our own. This would make a lot of sense,
366  // but we don't do it. Part of the rationale is that we can only
367  // operate on the tip of the order book. We can't leave an offer
368  // behind -- it would sit on the tip and block access to other
369  // offers.
370  //
371  // o We could delete the self-crossable offer(s) off the tip of the
372  // book and continue with offer crossing. That's what we do.
373  //
374  // To support this scenario offer crossing has a special rule. If:
375  // a. We're offer crossing using default path (no autobridging), and
376  // b. The offer's quality is at least as good as our quality, and
377  // c. We're about to cross one of our own offers, then
378  // d. Delete the old offer from the ledger.
379  if (defaultPath_ && offer.quality() >= qualityThreshold_ &&
380  strandSrc == offer.owner() && strandDst == offer.owner())
381  {
382  // Remove this offer even if no crossing occurs.
383  offers.permRmOffer(offer.key());
384 
385  // If no offers have been attempted yet then it's okay to move to
386  // a different quality.
387  if (!offerAttempted)
388  ofrQ = std::nullopt;
389 
390  // Return true so the current offer will be deleted.
391  return true;
392  }
393  return false;
394  }
395 
396  // Offer crossing can prune the offers it needs to look at with a
397  // quality threshold.
398  bool
400  {
401  return !defaultPath_ || offer.quality() >= qualityThreshold_;
402  }
403 
404  // For offer crossing don't pay the transfer fee if alice is paying alice.
405  // A regular (non-offer-crossing) payment does not apply this rule.
408  Step const* prevStep,
409  TOffer<TIn, TOut> const& offer,
410  std::uint32_t trIn) const
411  {
412  auto const srcAcct =
413  prevStep ? prevStep->directStepSrcAcct() : std::nullopt;
414 
415  return // If offer crossing
416  srcAcct && // && prevStep is DirectI
417  offer.owner() == *srcAcct // && src is offer owner
418  ? QUALITY_ONE
419  : trIn; // then rate = QUALITY_ONE
420  }
421 
422  // See comment on getOfrInRate().
425  Step const* prevStep,
426  TOffer<TIn, TOut> const& offer,
427  AccountID const& strandDst,
428  std::uint32_t trOut) const
429  {
430  return // If offer crossing
431  prevStep && prevStep->bookStepBook() && // && prevStep is BookStep
432  offer.owner() == strandDst // && dest is offer owner
433  ? QUALITY_ONE
434  : trOut; // then rate = QUALITY_ONE
435  }
436 
437  Quality
439  ReadView const& v,
440  Quality const& ofrQ,
441  DebtDirection prevStepDir) const
442  {
443  // Offer x-ing does not charge a transfer fee when the offer's owner
444  // is the same as the strand dst. It is important that
445  // `qualityUpperBound` is an upper bound on the quality (it is used to
446  // ignore strands whose quality cannot meet a minimum threshold). When
447  // calculating quality assume no fee is charged, or the estimate will no
448  // longer be an upper bound.
449  return ofrQ;
450  }
451 
453  logString() const override
454  {
455  return this->logStringImpl("BookOfferCrossingStep");
456  }
457 
458 private:
459  bool const defaultPath_;
460  Quality const qualityThreshold_;
461 };
462 
463 //------------------------------------------------------------------------------
464 
465 template <class TIn, class TOut, class TDerived>
466 bool
468 {
469  if (auto bs = dynamic_cast<BookStep<TIn, TOut, TDerived> const*>(&rhs))
470  return book_ == bs->book_;
471  return false;
472 }
473 
474 template <class TIn, class TOut, class TDerived>
477  ReadView const& v,
478  DebtDirection prevStepDir) const
479 {
480  auto const dir = this->debtDirection(v, StrandDirection::forward);
481 
482  // This can be simplified (and sped up) if directories are never empty.
483  Sandbox sb(&v, tapNONE);
484  BookTip bt(sb, book_);
485  if (!bt.step(j_))
486  return {std::nullopt, dir};
487 
488  Quality const q = static_cast<TDerived const*>(this)->adjustQualityWithFees(
489  v, bt.quality(), prevStepDir);
490  return {q, dir};
491 }
492 
493 template <class TIn, class TOut, class TDerived>
496 {
497  return offersUsed_;
498 }
499 
500 // Adjust the offer amount and step amount subject to the given input limit
501 template <class TIn, class TOut>
502 static void
504  Quality const& ofrQ,
505  TAmounts<TIn, TOut>& ofrAmt,
506  TAmounts<TIn, TOut>& stpAmt,
507  TOut& ownerGives,
508  std::uint32_t transferRateIn,
509  std::uint32_t transferRateOut,
510  TIn const& limit)
511 {
512  if (limit < stpAmt.in)
513  {
514  stpAmt.in = limit;
515  auto const inLmt =
516  mulRatio(stpAmt.in, QUALITY_ONE, transferRateIn, /*roundUp*/ false);
517  ofrAmt = ofrQ.ceil_in(ofrAmt, inLmt);
518  stpAmt.out = ofrAmt.out;
519  ownerGives = mulRatio(
520  ofrAmt.out, transferRateOut, QUALITY_ONE, /*roundUp*/ false);
521  }
522 }
523 
524 // Adjust the offer amount and step amount subject to the given output limit
525 template <class TIn, class TOut>
526 static void
528  Quality const& ofrQ,
529  TAmounts<TIn, TOut>& ofrAmt,
530  TAmounts<TIn, TOut>& stpAmt,
531  TOut& ownerGives,
532  std::uint32_t transferRateIn,
533  std::uint32_t transferRateOut,
534  TOut const& limit,
535  Rules const& rules)
536 {
537  if (limit < stpAmt.out)
538  {
539  stpAmt.out = limit;
540  ownerGives = mulRatio(
541  stpAmt.out, transferRateOut, QUALITY_ONE, /*roundUp*/ false);
542  if (rules.enabled(fixReducedOffersV1))
543  // It turns out that the ceil_out implementation has some slop in
544  // it. ceil_out_strict removes that slop. But removing that slop
545  // affects transaction outcomes, so the change must be made using
546  // an amendment.
547  ofrAmt = ofrQ.ceil_out_strict(ofrAmt, stpAmt.out, /*roundUp*/ true);
548  else
549  ofrAmt = ofrQ.ceil_out(ofrAmt, stpAmt.out);
550  stpAmt.in =
551  mulRatio(ofrAmt.in, transferRateIn, QUALITY_ONE, /*roundUp*/ true);
552  }
553 }
554 
555 template <class TIn, class TOut, class TDerived>
556 template <class Callback>
559  PaymentSandbox& sb,
560  ApplyView& afView,
561  DebtDirection prevStepDir,
562  Callback& callback) const
563 {
564  // Charge the offer owner, not the sender
565  // Charge a fee even if the owner is the same as the issuer
566  // (the old code does not charge a fee)
567  // Calculate amount that goes to the taker and the amount charged the offer
568  // owner
569  auto rate = [this, &sb](AccountID const& id) -> std::uint32_t {
570  if (isXRP(id) || id == this->strandDst_)
571  return QUALITY_ONE;
572  return transferRate(sb, id).value;
573  };
574 
575  std::uint32_t const trIn =
576  redeems(prevStepDir) ? rate(book_.in.account) : QUALITY_ONE;
577  // Always charge the transfer fee, even if the owner is the issuer
578  std::uint32_t const trOut =
579  ownerPaysTransferFee_ ? rate(book_.out.account) : QUALITY_ONE;
580 
582  maxOffersToConsume_, j_);
583 
585  sb, afView, book_, sb.parentCloseTime(), counter, j_);
586 
587  bool const flowCross = afView.rules().enabled(featureFlowCross);
588  bool const fixReduced = afView.rules().enabled(fixReducedOffersV1);
589  bool offerAttempted = false;
591  while (offers.step())
592  {
593  auto& offer = offers.tip();
594 
595  // Note that offer.quality() returns a (non-optional) Quality. So
596  // ofrQ is always safe to use below this point in the loop.
597  if (!ofrQ)
598  ofrQ = offer.quality();
599  else if (*ofrQ != offer.quality())
600  break;
601 
602  if (static_cast<TDerived const*>(this)->limitSelfCrossQuality(
603  strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
604  continue;
605 
606  // Make sure offer owner has authorization to own IOUs from issuer.
607  // An account can always own XRP or their own IOUs.
608  if (flowCross && (!isXRP(offer.issueIn().currency)) &&
609  (offer.owner() != offer.issueIn().account))
610  {
611  auto const& issuerID = offer.issueIn().account;
612  auto const issuer = afView.read(keylet::account(issuerID));
613  if (issuer && ((*issuer)[sfFlags] & lsfRequireAuth))
614  {
615  // Issuer requires authorization. See if offer owner has that.
616  auto const& ownerID = offer.owner();
617  auto const authFlag =
618  issuerID > ownerID ? lsfHighAuth : lsfLowAuth;
619 
620  auto const line = afView.read(
621  keylet::line(ownerID, issuerID, offer.issueIn().currency));
622 
623  if (!line || (((*line)[sfFlags] & authFlag) == 0))
624  {
625  // Offer owner not authorized to hold IOU from issuer.
626  // Remove this offer even if no crossing occurs.
627  offers.permRmOffer(offer.key());
628  if (!offerAttempted)
629  // Change quality only if no previous offers were tried.
630  ofrQ = std::nullopt;
631  // This continue causes offers.step() to delete the offer.
632  continue;
633  }
634  }
635  }
636 
637  if (!static_cast<TDerived const*>(this)->checkQualityThreshold(offer))
638  break;
639 
640  auto const ofrInRate = static_cast<TDerived const*>(this)->getOfrInRate(
641  prevStep_, offer, trIn);
642 
643  auto const ofrOutRate =
644  static_cast<TDerived const*>(this)->getOfrOutRate(
645  prevStep_, offer, strandDst_, trOut);
646 
647  auto ofrAmt = offer.amount();
648  TAmounts stpAmt{
649  mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE, /*roundUp*/ true),
650  ofrAmt.out};
651 
652  // owner pays the transfer fee.
653  auto ownerGives =
654  mulRatio(ofrAmt.out, ofrOutRate, QUALITY_ONE, /*roundUp*/ false);
655 
656  auto const funds = (offer.owner() == offer.issueOut().account)
657  ? ownerGives // Offer owner is issuer; they have unlimited funds
658  : offers.ownerFunds();
659 
660  if (funds < ownerGives)
661  {
662  // We already know offer.owner()!=offer.issueOut().account
663  ownerGives = funds;
664  stpAmt.out = mulRatio(
665  ownerGives, QUALITY_ONE, ofrOutRate, /*roundUp*/ false);
666 
667  // It turns out we can prevent order book blocking by (strictly)
668  // rounding down the ceil_out() result. This adjustment changes
669  // transaction outcomes, so it must be made under an amendment.
670  if (fixReduced)
671  ofrAmt = ofrQ->ceil_out_strict(
672  ofrAmt, stpAmt.out, /* roundUp */ false);
673  else
674  ofrAmt = ofrQ->ceil_out(ofrAmt, stpAmt.out);
675 
676  stpAmt.in =
677  mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE, /*roundUp*/ true);
678  }
679 
680  offerAttempted = true;
681  if (!callback(offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate))
682  break;
683  }
684 
685  return {offers.permToRemove(), counter.count()};
686 }
687 
688 template <class TIn, class TOut, class TDerived>
689 void
691  PaymentSandbox& sb,
692  TOffer<TIn, TOut>& offer,
693  TAmounts<TIn, TOut> const& ofrAmt,
694  TAmounts<TIn, TOut> const& stepAmt,
695  TOut const& ownerGives) const
696 {
697  // The offer owner gets the ofrAmt. The difference between ofrAmt and
698  // stepAmt is a transfer fee that goes to book_.in.account
699  {
700  auto const dr = accountSend(
701  sb,
702  book_.in.account,
703  offer.owner(),
704  toSTAmount(ofrAmt.in, book_.in),
705  j_);
706  if (dr != tesSUCCESS)
707  Throw<FlowException>(dr);
708  }
709 
710  // The offer owner pays `ownerGives`. The difference between ownerGives and
711  // stepAmt is a transfer fee that goes to book_.out.account
712  {
713  auto const cr = accountSend(
714  sb,
715  offer.owner(),
716  book_.out.account,
717  toSTAmount(ownerGives, book_.out),
718  j_);
719  if (cr != tesSUCCESS)
720  Throw<FlowException>(cr);
721  }
722 
723  offer.consume(sb, ofrAmt);
724 }
725 
726 template <class TCollection>
727 static auto
728 sum(TCollection const& col)
729 {
730  using TResult = std::decay_t<decltype(*col.begin())>;
731  if (col.empty())
732  return TResult{beast::zero};
733  return std::accumulate(col.begin() + 1, col.end(), *col.begin());
734 };
735 
736 template <class TIn, class TOut, class TDerived>
739  PaymentSandbox& sb,
740  ApplyView& afView,
741  boost::container::flat_set<uint256>& ofrsToRm,
742  TOut const& out)
743 {
744  cache_.reset();
745 
746  TAmounts<TIn, TOut> result(beast::zero, beast::zero);
747 
748  auto remainingOut = out;
749 
750  boost::container::flat_multiset<TIn> savedIns;
751  savedIns.reserve(64);
752  boost::container::flat_multiset<TOut> savedOuts;
753  savedOuts.reserve(64);
754 
755  /* amt fed will be adjusted by owner funds (and may differ from the offer's
756  amounts - tho always <=)
757  Return true to continue to receive offers, false to stop receiving offers.
758  */
759  auto eachOffer = [&](TOffer<TIn, TOut>& offer,
760  TAmounts<TIn, TOut> const& ofrAmt,
761  TAmounts<TIn, TOut> const& stpAmt,
762  TOut const& ownerGives,
763  std::uint32_t transferRateIn,
764  std::uint32_t transferRateOut) mutable -> bool {
765  if (remainingOut <= beast::zero)
766  return false;
767 
768  if (stpAmt.out <= remainingOut)
769  {
770  savedIns.insert(stpAmt.in);
771  savedOuts.insert(stpAmt.out);
772  result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
773  remainingOut = out - result.out;
774  this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
775  // return true b/c even if the payment is satisfied,
776  // we need to consume the offer
777  return true;
778  }
779  else
780  {
781  auto ofrAdjAmt = ofrAmt;
782  auto stpAdjAmt = stpAmt;
783  auto ownerGivesAdj = ownerGives;
784  limitStepOut(
785  offer.quality(),
786  ofrAdjAmt,
787  stpAdjAmt,
788  ownerGivesAdj,
789  transferRateIn,
790  transferRateOut,
791  remainingOut,
792  afView.rules());
793  remainingOut = beast::zero;
794  savedIns.insert(stpAdjAmt.in);
795  savedOuts.insert(remainingOut);
796  result.in = sum(savedIns);
797  result.out = out;
798  this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
799 
800  // Explicitly check whether the offer is funded. Given that we have
801  // (stpAmt.out > remainingOut), it's natural to assume the offer
802  // will still be funded after consuming remainingOut but that is
803  // not always the case. If the mantissas of two IOU amounts differ
804  // by less than ten, then subtracting them leaves a zero.
805  return offer.fully_consumed();
806  }
807  };
808 
809  {
810  auto const prevStepDebtDir = [&] {
811  if (prevStep_)
812  return prevStep_->debtDirection(sb, StrandDirection::reverse);
813  return DebtDirection::issues;
814  }();
815  auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
816  boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
817  std::uint32_t const offersConsumed = std::get<1>(r);
818  offersUsed_ = offersConsumed;
819  SetUnion(ofrsToRm, toRm);
820 
821  if (offersConsumed >= maxOffersToConsume_)
822  {
823  // Too many iterations, mark this strand as inactive
824  if (!afView.rules().enabled(fix1515))
825  {
826  // Don't use the liquidity
827  cache_.emplace(beast::zero, beast::zero);
828  return {beast::zero, beast::zero};
829  }
830 
831  // Use the liquidity, but use this to mark the strand as inactive so
832  // it's not used further
833  inactive_ = true;
834  }
835  }
836 
837  switch (remainingOut.signum())
838  {
839  case -1: {
840  // something went very wrong
841  JLOG(j_.error())
842  << "BookStep remainingOut < 0 " << to_string(remainingOut);
843  assert(0);
844  cache_.emplace(beast::zero, beast::zero);
845  return {beast::zero, beast::zero};
846  }
847  case 0: {
848  // due to normalization, remainingOut can be zero without
849  // result.out == out. Force result.out == out for this case
850  result.out = out;
851  }
852  }
853 
854  cache_.emplace(result.in, result.out);
855  return {result.in, result.out};
856 }
857 
858 template <class TIn, class TOut, class TDerived>
861  PaymentSandbox& sb,
862  ApplyView& afView,
863  boost::container::flat_set<uint256>& ofrsToRm,
864  TIn const& in)
865 {
866  assert(cache_);
867 
868  TAmounts<TIn, TOut> result(beast::zero, beast::zero);
869 
870  auto remainingIn = in;
871 
872  boost::container::flat_multiset<TIn> savedIns;
873  savedIns.reserve(64);
874  boost::container::flat_multiset<TOut> savedOuts;
875  savedOuts.reserve(64);
876 
877  // amt fed will be adjusted by owner funds (and may differ from the offer's
878  // amounts - tho always <=)
879  auto eachOffer = [&](TOffer<TIn, TOut>& offer,
880  TAmounts<TIn, TOut> const& ofrAmt,
881  TAmounts<TIn, TOut> const& stpAmt,
882  TOut const& ownerGives,
883  std::uint32_t transferRateIn,
884  std::uint32_t transferRateOut) mutable -> bool {
885  assert(cache_);
886 
887  if (remainingIn <= beast::zero)
888  return false;
889 
890  bool processMore = true;
891  auto ofrAdjAmt = ofrAmt;
892  auto stpAdjAmt = stpAmt;
893  auto ownerGivesAdj = ownerGives;
894 
895  typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
896  if (stpAmt.in <= remainingIn)
897  {
898  savedIns.insert(stpAmt.in);
899  lastOut = savedOuts.insert(stpAmt.out);
900  result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
901  // consume the offer even if stepAmt.in == remainingIn
902  processMore = true;
903  }
904  else
905  {
906  limitStepIn(
907  offer.quality(),
908  ofrAdjAmt,
909  stpAdjAmt,
910  ownerGivesAdj,
911  transferRateIn,
912  transferRateOut,
913  remainingIn);
914  savedIns.insert(remainingIn);
915  lastOut = savedOuts.insert(stpAdjAmt.out);
916  result.out = sum(savedOuts);
917  result.in = in;
918 
919  processMore = false;
920  }
921 
922  if (result.out > cache_->out && result.in <= cache_->in)
923  {
924  // The step produced more output in the forward pass than the
925  // reverse pass while consuming the same input (or less). If we
926  // compute the input required to produce the cached output
927  // (produced in the reverse step) and the input is equal to
928  // the input consumed in the forward step, then consume the
929  // input provided in the forward step and produce the output
930  // requested from the reverse step.
931  auto const lastOutAmt = *lastOut;
932  savedOuts.erase(lastOut);
933  auto const remainingOut = cache_->out - sum(savedOuts);
934  auto ofrAdjAmtRev = ofrAmt;
935  auto stpAdjAmtRev = stpAmt;
936  auto ownerGivesAdjRev = ownerGives;
937  limitStepOut(
938  offer.quality(),
939  ofrAdjAmtRev,
940  stpAdjAmtRev,
941  ownerGivesAdjRev,
942  transferRateIn,
943  transferRateOut,
944  remainingOut,
945  afView.rules());
946 
947  if (stpAdjAmtRev.in == remainingIn)
948  {
949  result.in = in;
950  result.out = cache_->out;
951 
952  savedIns.clear();
953  savedIns.insert(result.in);
954  savedOuts.clear();
955  savedOuts.insert(result.out);
956 
957  ofrAdjAmt = ofrAdjAmtRev;
958  stpAdjAmt.in = remainingIn;
959  stpAdjAmt.out = remainingOut;
960  ownerGivesAdj = ownerGivesAdjRev;
961  }
962  else
963  {
964  // This is (likely) a problem case, and wil be caught
965  // with later checks
966  savedOuts.insert(lastOutAmt);
967  }
968  }
969 
970  remainingIn = in - result.in;
971  this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
972 
973  // When the mantissas of two iou amounts differ by less than ten, then
974  // subtracting them leaves a result of zero. This can cause the check
975  // for (stpAmt.in > remainingIn) to incorrectly think an offer will be
976  // funded after subtracting remainingIn.
977  return processMore || offer.fully_consumed();
978  };
979 
980  {
981  auto const prevStepDebtDir = [&] {
982  if (prevStep_)
983  return prevStep_->debtDirection(sb, StrandDirection::forward);
984  return DebtDirection::issues;
985  }();
986  auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
987  boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
988  std::uint32_t const offersConsumed = std::get<1>(r);
989  offersUsed_ = offersConsumed;
990  SetUnion(ofrsToRm, toRm);
991 
992  if (offersConsumed >= maxOffersToConsume_)
993  {
994  // Too many iterations, mark this strand as inactive (dry)
995  if (!afView.rules().enabled(fix1515))
996  {
997  // Don't use the liquidity
998  cache_.emplace(beast::zero, beast::zero);
999  return {beast::zero, beast::zero};
1000  }
1001 
1002  // Use the liquidity, but use this to mark the strand as inactive so
1003  // it's not used further
1004  inactive_ = true;
1005  }
1006  }
1007 
1008  switch (remainingIn.signum())
1009  {
1010  case -1: {
1011  // something went very wrong
1012  JLOG(j_.error())
1013  << "BookStep remainingIn < 0 " << to_string(remainingIn);
1014  assert(0);
1015  cache_.emplace(beast::zero, beast::zero);
1016  return {beast::zero, beast::zero};
1017  }
1018  case 0: {
1019  // due to normalization, remainingIn can be zero without
1020  // result.in == in. Force result.in == in for this case
1021  result.in = in;
1022  }
1023  }
1024 
1025  cache_.emplace(result.in, result.out);
1026  return {result.in, result.out};
1027 }
1028 
1029 template <class TIn, class TOut, class TDerived>
1032  PaymentSandbox& sb,
1033  ApplyView& afView,
1034  EitherAmount const& in)
1035 {
1036  if (!cache_)
1037  {
1038  JLOG(j_.trace()) << "Expected valid cache in validFwd";
1039  return {false, EitherAmount(TOut(beast::zero))};
1040  }
1041 
1042  auto const savCache = *cache_;
1043 
1044  try
1045  {
1046  boost::container::flat_set<uint256> dummy;
1047  fwdImp(sb, afView, dummy, get<TIn>(in)); // changes cache
1048  }
1049  catch (FlowException const&)
1050  {
1051  return {false, EitherAmount(TOut(beast::zero))};
1052  }
1053 
1054  if (!(checkNear(savCache.in, cache_->in) &&
1055  checkNear(savCache.out, cache_->out)))
1056  {
1057  JLOG(j_.warn()) << "Strand re-execute check failed."
1058  << " ExpectedIn: " << to_string(savCache.in)
1059  << " CachedIn: " << to_string(cache_->in)
1060  << " ExpectedOut: " << to_string(savCache.out)
1061  << " CachedOut: " << to_string(cache_->out);
1062  return {false, EitherAmount(cache_->out)};
1063  }
1064  return {true, EitherAmount(cache_->out)};
1065 }
1066 
1067 template <class TIn, class TOut, class TDerived>
1068 TER
1070 {
1071  if (book_.in == book_.out)
1072  {
1073  JLOG(j_.debug()) << "BookStep: Book with same in and out issuer "
1074  << *this;
1075  return temBAD_PATH;
1076  }
1077  if (!isConsistent(book_.in) || !isConsistent(book_.out))
1078  {
1079  JLOG(j_.debug()) << "Book: currency is inconsistent with issuer."
1080  << *this;
1081  return temBAD_PATH;
1082  }
1083 
1084  // Do not allow two books to output the same issue. This may cause offers on
1085  // one step to unfund offers in another step.
1086  if (!ctx.seenBookOuts.insert(book_.out).second ||
1087  ctx.seenDirectIssues[0].count(book_.out))
1088  {
1089  JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
1090  return temBAD_PATH_LOOP;
1091  }
1092 
1093  if (ctx.seenDirectIssues[1].count(book_.out))
1094  {
1095  JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
1096  return temBAD_PATH_LOOP;
1097  }
1098 
1099  auto issuerExists = [](ReadView const& view, Issue const& iss) -> bool {
1100  return isXRP(iss.account) || view.read(keylet::account(iss.account));
1101  };
1102 
1103  if (!issuerExists(ctx.view, book_.in) || !issuerExists(ctx.view, book_.out))
1104  {
1105  JLOG(j_.debug()) << "BookStep: deleted issuer detected: " << *this;
1106  return tecNO_ISSUER;
1107  }
1108 
1109  if (ctx.prevStep)
1110  {
1111  if (auto const prev = ctx.prevStep->directStepSrcAcct())
1112  {
1113  auto const& view = ctx.view;
1114  auto const& cur = book_.in.account;
1115 
1116  auto sle = view.read(keylet::line(*prev, cur, book_.in.currency));
1117  if (!sle)
1118  return terNO_LINE;
1119  if ((*sle)[sfFlags] &
1120  ((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple))
1121  return terNO_RIPPLE;
1122  }
1123  }
1124 
1125  return tesSUCCESS;
1126 }
1127 
1128 //------------------------------------------------------------------------------
1129 
1130 namespace test {
1131 // Needed for testing
1132 
1133 template <class TIn, class TOut, class TDerived>
1134 static bool
1135 equalHelper(Step const& step, ripple::Book const& book)
1136 {
1137  if (auto bs = dynamic_cast<BookStep<TIn, TOut, TDerived> const*>(&step))
1138  return book == bs->book();
1139  return false;
1140 }
1141 
1142 bool
1143 bookStepEqual(Step const& step, ripple::Book const& book)
1144 {
1145  bool const inXRP = isXRP(book.in.currency);
1146  bool const outXRP = isXRP(book.out.currency);
1147  if (inXRP && outXRP)
1148  {
1149  assert(0);
1150  return false; // no such thing as xrp/xrp book step
1151  }
1152  if (inXRP && !outXRP)
1153  return equalHelper<
1154  XRPAmount,
1155  IOUAmount,
1157  if (!inXRP && outXRP)
1158  return equalHelper<
1159  IOUAmount,
1160  XRPAmount,
1162  if (!inXRP && !outXRP)
1163  return equalHelper<
1164  IOUAmount,
1165  IOUAmount,
1167  return false;
1168 }
1169 } // namespace test
1170 
1171 //------------------------------------------------------------------------------
1172 
1173 template <class TIn, class TOut>
1175 make_BookStepHelper(StrandContext const& ctx, Issue const& in, Issue const& out)
1176 {
1177  TER ter = tefINTERNAL;
1179  if (ctx.offerCrossing)
1180  {
1181  auto offerCrossingStep =
1182  std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx, in, out);
1183  ter = offerCrossingStep->check(ctx);
1184  r = std::move(offerCrossingStep);
1185  }
1186  else // payment
1187  {
1188  auto paymentStep =
1189  std::make_unique<BookPaymentStep<TIn, TOut>>(ctx, in, out);
1190  ter = paymentStep->check(ctx);
1191  r = std::move(paymentStep);
1192  }
1193  if (ter != tesSUCCESS)
1194  return {ter, nullptr};
1195 
1196  return {tesSUCCESS, std::move(r)};
1197 }
1198 
1200 make_BookStepII(StrandContext const& ctx, Issue const& in, Issue const& out)
1201 {
1202  return make_BookStepHelper<IOUAmount, IOUAmount>(ctx, in, out);
1203 }
1204 
1206 make_BookStepIX(StrandContext const& ctx, Issue const& in)
1207 {
1208  return make_BookStepHelper<IOUAmount, XRPAmount>(ctx, in, xrpIssue());
1209 }
1210 
1212 make_BookStepXI(StrandContext const& ctx, Issue const& out)
1213 {
1214  return make_BookStepHelper<XRPAmount, IOUAmount>(ctx, xrpIssue(), out);
1215 }
1216 
1217 } // namespace ripple
ripple::mulRatio
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Definition: IOUAmount.cpp:182
ripple::BookStep::offersUsed
std::uint32_t offersUsed() const override
Definition: BookStep.cpp:495
ripple::StrandContext
Context needed to build Strand Steps and for error checking.
Definition: Steps.h:497
ripple::transferRate
Rate transferRate(ReadView const &view, AccountID const &issuer)
Definition: View.cpp:471
sstream
ripple::BookOfferCrossingStep::logString
std::string logString() const override
Definition: BookStep.cpp:453
ripple::TOfferStreamBase::StepCounter
Definition: OfferStream.h:39
ripple::BookOfferCrossingStep::getOfrInRate
std::uint32_t getOfrInRate(Step const *prevStep, TOffer< TIn, TOut > const &offer, std::uint32_t trIn) const
Definition: BookStep.cpp:407
ripple::BookStep::logStringImpl
std::string logStringImpl(char const *name) const
Definition: BookStep.cpp:168
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
ripple::isConsistent
bool isConsistent(Book const &book)
Definition: Book.cpp:25
ripple::FlowOfferStream
Presents and consumes the offers in an order book.
Definition: OfferStream.h:175
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::composed_quality
Quality composed_quality(Quality const &lhs, Quality const &rhs)
Definition: Quality.cpp:124
ripple::Book::out
Issue out
Definition: Book.h:37
ripple::terNO_LINE
@ terNO_LINE
Definition: TER.h:200
ripple::DebtDirection
DebtDirection
Definition: Steps.h:37
ripple::BookStep::forEachOffer
std::pair< boost::container::flat_set< uint256 >, std::uint32_t > forEachOffer(PaymentSandbox &sb, ApplyView &afView, DebtDirection prevStepDebtDir, Callback &callback) const
Definition: BookStep.cpp:558
ripple::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:112
ripple::make_BookStepXI
std::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
Definition: BookStep.cpp:1212
ripple::make_BookStepII
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1200
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::test::equalHelper
static bool equalHelper(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1135
ripple::BookPaymentStep::getOfrInRate
std::uint32_t getOfrInRate(Step const *, TOffer< TIn, TOut > const &, std::uint32_t trIn) const
Definition: BookStep.cpp:257
ripple::fix1515
const uint256 fix1515
ripple::lsfLowAuth
@ lsfLowAuth
Definition: LedgerFormats.h:257
ripple::lsfLowNoRipple
@ lsfLowNoRipple
Definition: LedgerFormats.h:259
ripple::BookPaymentStep::BookPaymentStep
BookPaymentStep()=default
ripple::fixReducedOffersV1
const uint256 fixReducedOffersV1
ripple::BookOfferCrossingStep
Definition: BookStep.cpp:311
ripple::BookStep::maxOffersToConsume_
const uint32_t maxOffersToConsume_
Definition: BookStep.cpp:45
ripple::isDefaultPath
static bool isDefaultPath(STPath const &path)
Definition: Pathfinder.cpp:455
ripple::BookStep::book
Book const & book() const
Definition: BookStep.cpp:97
ripple::checkNear
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition: PaySteps.cpp:36
ripple::BookPaymentStep
Definition: BookStep.cpp:227
ripple::Issue::currency
Currency currency
Definition: Issue.h:37
ripple::BookStep::qualityUpperBound
std::pair< std::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection prevStepDir) const override
Definition: BookStep.cpp:476
ripple::BookStep::inactive
bool inactive() const override
Definition: BookStep.cpp:161
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::QualityDirection::in
@ in
ripple::BookOfferCrossingStep::limitSelfCrossQuality
bool limitSelfCrossQuality(AccountID const &strandSrc, AccountID const &strandDst, TOffer< TIn, TOut > const &offer, std::optional< Quality > &ofrQ, FlowOfferStream< TIn, TOut > &offers, bool const offerAttempted) const
Definition: BookStep.cpp:342
ripple::limitStepIn
static void limitStepIn(Quality const &ofrQ, TAmounts< TIn, TOut > &ofrAmt, TAmounts< TIn, TOut > &stpAmt, TOut &ownerGives, std::uint32_t transferRateIn, std::uint32_t transferRateOut, TIn const &limit)
Definition: BookStep.cpp:503
ripple::IOUAmount
Floating point representation of amounts with high dynamic range.
Definition: IOUAmount.h:43
ripple::terNO_RIPPLE
@ terNO_RIPPLE
Definition: TER.h:205
ripple::BookOfferCrossingStep::BookOfferCrossingStep
BookOfferCrossingStep(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:331
ripple::BookStep::operator!=
friend bool operator!=(BookStep const &lhs, BookStep const &rhs)
Definition: BookStep.cpp:187
ripple::ReadView::parentCloseTime
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition: ReadView.h:186
ripple::BookStep::Cache
Definition: BookStep.cpp:64
ripple::tapNONE
@ tapNONE
Definition: ApplyView.h:30
ripple::temBAD_PATH
@ temBAD_PATH
Definition: TER.h:94
ripple::BookStep::strandDst_
AccountID strandDst_
Definition: BookStep.cpp:48
ripple::BookStep::validFwd
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
Definition: BookStep.cpp:1031
ripple::test::bookStepEqual
bool bookStepEqual(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1143
ripple::BookStep::inactive_
bool inactive_
Definition: BookStep.cpp:53
ripple::parityRate
const Rate parityRate(QUALITY_ONE)
A transfer rate signifying a 1:1 exchange.
Definition: Rate.h:101
ripple::BookStep::j_
const beast::Journal j_
Definition: BookStep.cpp:62
ripple::ApplyView
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:134
ripple::BookOfferCrossingStep::defaultPath_
const bool defaultPath_
Definition: BookStep.cpp:459
ripple::lsfHighAuth
@ lsfHighAuth
Definition: LedgerFormats.h:258
ripple::StrandDirection
StrandDirection
Definition: Steps.h:39
ripple::BookOfferCrossingStep::getOfrOutRate
std::uint32_t getOfrOutRate(Step const *prevStep, TOffer< TIn, TOut > const &offer, AccountID const &strandDst, std::uint32_t trOut) const
Definition: BookStep.cpp:424
ripple::BookStep::debtDirection
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
Definition: BookStep.cpp:119
ripple::BookPaymentStep::limitSelfCrossQuality
bool limitSelfCrossQuality(AccountID const &, AccountID const &, TOffer< TIn, TOut > const &offer, std::optional< Quality > &, FlowOfferStream< TIn, TOut > &, bool) const
Definition: BookStep.cpp:237
ripple::getRate
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition: STAmount.cpp:495
ripple::BookStep::ownerPaysTransferFee_
const bool ownerPaysTransferFee_
Definition: BookStep.cpp:51
ripple::BookPaymentStep::adjustQualityWithFees
Quality adjustQualityWithFees(ReadView const &v, Quality const &ofrQ, DebtDirection prevStepDir) const
Definition: BookStep.cpp:275
ripple::Keylet::key
uint256 key
Definition: Keylet.h:40
ripple::base_uint< 160, detail::AccountIDTag >
ripple::BookOfferCrossingStep::getQuality
static Quality getQuality(std::optional< Quality > const &limitQuality)
Definition: BookStep.cpp:321
ripple::lsfRequireAuth
@ lsfRequireAuth
Definition: LedgerFormats.h:226
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::BookStep::consumeOffer
void consumeOffer(PaymentSandbox &sb, TOffer< TIn, TOut > &offer, TAmounts< TIn, TOut > const &ofrAmt, TAmounts< TIn, TOut > const &stepAmt, TOut const &ownerGives) const
Definition: BookStep.cpp:690
ripple::QualityDirection::out
@ out
ripple::BookPaymentStep::checkQualityThreshold
bool checkQualityThreshold(TOffer< TIn, TOut > const &offer) const
Definition: BookStep.cpp:250
ripple::BookStep::fwdImp
std::pair< TIn, TOut > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, TIn const &in)
Definition: BookStep.cpp:860
ripple::BookStep::BookStep
BookStep(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:85
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:133
ripple::BookStep::check
TER check(StrandContext const &ctx) const
Definition: BookStep.cpp:1069
ripple::StrandContext::offerCrossing
const bool offerCrossing
true if offer crossing, not payment
Definition: Steps.h:507
ripple::BookStep::cachedIn
std::optional< EitherAmount > cachedIn() const override
Definition: BookStep.cpp:103
ripple::BookTip::quality
Quality const & quality() const noexcept
Definition: BookTip.h:66
ripple::toSTAmount
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
Definition: AmountConversions.h:30
ripple::BookStep::strandSrc_
AccountID strandSrc_
Definition: BookStep.cpp:47
ripple::TERSubset< CanCvtToTER >
ripple::Sandbox
Discardable, editable view to a ledger.
Definition: Sandbox.h:34
ripple::BookOfferCrossingStep::qualityThreshold_
const Quality qualityThreshold_
Definition: BookStep.cpp:460
ripple::Step
A step in a payment path.
Definition: Steps.h:79
ripple::BookTip
Iterates and consumes raw offers in an order book.
Definition: BookTip.h:37
ripple::accountSend
TER accountSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j)
Definition: View.cpp:1122
ripple::DebtDirection::redeems
@ redeems
ripple::STAmount
Definition: STAmount.h:45
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::BookStep::getMaxOffersToConsume
static uint32_t getMaxOffersToConsume(StrandContext const &ctx)
Definition: BookStep.cpp:77
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::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:89
std::accumulate
T accumulate(T... args)
ripple::BookStep::Cache::Cache
Cache(TIn const &in_, TOut const &out_)
Definition: BookStep.cpp:69
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
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::lsfHighNoRipple
@ lsfHighNoRipple
Definition: LedgerFormats.h:260
ripple::BookStep::operator==
friend bool operator==(BookStep const &lhs, BookStep const &rhs)
Definition: BookStep.cpp:181
ripple::BookStep::cachedOut
std::optional< EitherAmount > cachedOut() const override
Definition: BookStep.cpp:111
std::decay_t
ripple::StrandDirection::reverse
@ reverse
ripple::DebtDirection::issues
@ issues
std::ostringstream
STL class.
ripple::BookPaymentStep::getOfrOutRate
std::uint32_t getOfrOutRate(Step const *, TOffer< TIn, TOut > const &, AccountID const &, std::uint32_t trOut) const
Definition: BookStep.cpp:265
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::BookStep::prevStep_
Step const *const prevStep_
Definition: BookStep.cpp:50
ripple::BookStep
Definition: BookStep.cpp:42
ripple::BookStep::offersUsed_
std::uint32_t offersUsed_
Number of offers consumed or partially consumed the last time the step ran, including expired and unf...
Definition: BookStep.cpp:61
ripple::BookOfferCrossingStep::checkQualityThreshold
bool checkQualityThreshold(TOffer< TIn, TOut > const &offer) const
Definition: BookStep.cpp:399
ripple::make_BookStepHelper
static std::pair< TER, std::unique_ptr< Step > > make_BookStepHelper(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1175
ripple::ReadView::rules
virtual Rules const & rules() const =0
Returns the tx processing rules.
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::BookStep::book_
Book book_
Definition: BookStep.cpp:46
ripple::tecNO_ISSUER
@ tecNO_ISSUER
Definition: TER.h:266
ripple::EitherAmount
Definition: AmountSpec.h:59
ripple::StrandContext::prevStep
Step const *const prevStep
The previous step in the strand.
Definition: Steps.h:513
ripple::BookStep::bookStepBook
std::optional< Book > bookStepBook() const override
Definition: BookStep.cpp:126
ripple::xrpIssue
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:95
ripple::Rules
Rules controlling protocol behavior.
Definition: Rules.h:33
ripple::SetUnion
void SetUnion(boost::container::flat_set< T > &dst, boost::container::flat_set< T > const &src)
Given two flat sets dst and src, compute dst = dst union src.
Definition: FlatSets.h:35
std::optional
std::ostringstream::str
T str(T... args)
ripple::BookTip::step
bool step(beast::Journal j)
Erases the current offer and advance to the next offer.
Definition: BookTip.cpp:34
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
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::TOfferStreamBase::StepCounter::count
std::uint32_t count() const
Definition: OfferStream.h:64
ripple::BookStep::revImp
std::pair< TIn, TOut > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, TOut const &out)
Definition: BookStep.cpp:738
ripple::Book
Specifies an order book.
Definition: Book.h:33
ripple::temBAD_PATH_LOOP
@ temBAD_PATH_LOOP
Definition: TER.h:95
ripple::BookStep::cache_
std::optional< Cache > cache_
Definition: BookStep.cpp:74
numeric
ripple::BookStep::equal
bool equal(Step const &rhs) const override
Definition: BookStep.cpp:467
ripple::make_BookStepIX
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
Definition: BookStep.cpp:1206
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::featureFlowCross
const uint256 featureFlowCross
ripple::sum
static auto sum(TCollection const &col)
Definition: BookStep.cpp:728
ripple::BookStep::Cache::in
TIn in
Definition: BookStep.cpp:66
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:222
ripple::BookPaymentStep::logString
std::string logString() const override
Definition: BookStep.cpp:303
ripple::TOffer
Definition: Offer.h:49
ripple::Book::in
Issue in
Definition: Book.h:36
ripple::StrandDirection::forward
@ forward
ripple::Issue::account
AccountID account
Definition: Issue.h:38
ripple::BookStep::Cache::out
TOut out
Definition: BookStep.cpp:67
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::limitStepOut
static void limitStepOut(Quality const &ofrQ, TAmounts< TIn, TOut > &ofrAmt, TAmounts< TIn, TOut > &stpAmt, TOut &ownerGives, std::uint32_t transferRateIn, std::uint32_t transferRateOut, TOut const &limit, Rules const &rules)
Definition: BookStep.cpp:527
ripple::BookOfferCrossingStep::adjustQualityWithFees
Quality adjustQualityWithFees(ReadView const &v, Quality const &ofrQ, DebtDirection prevStepDir) const
Definition: BookStep.cpp:438