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 {
536  if (limit < stpAmt.out)
537  {
538  stpAmt.out = limit;
539  ownerGives = mulRatio(
540  stpAmt.out, transferRateOut, QUALITY_ONE, /*roundUp*/ false);
541  ofrAmt = ofrQ.ceil_out(ofrAmt, stpAmt.out);
542  stpAmt.in =
543  mulRatio(ofrAmt.in, transferRateIn, QUALITY_ONE, /*roundUp*/ true);
544  }
545 }
546 
547 template <class TIn, class TOut, class TDerived>
548 template <class Callback>
551  PaymentSandbox& sb,
552  ApplyView& afView,
553  DebtDirection prevStepDir,
554  Callback& callback) const
555 {
556  // Charge the offer owner, not the sender
557  // Charge a fee even if the owner is the same as the issuer
558  // (the old code does not charge a fee)
559  // Calculate amount that goes to the taker and the amount charged the offer
560  // owner
561  auto rate = [this, &sb](AccountID const& id) -> std::uint32_t {
562  if (isXRP(id) || id == this->strandDst_)
563  return QUALITY_ONE;
564  return transferRate(sb, id).value;
565  };
566 
567  std::uint32_t const trIn =
568  redeems(prevStepDir) ? rate(book_.in.account) : QUALITY_ONE;
569  // Always charge the transfer fee, even if the owner is the issuer
570  std::uint32_t const trOut =
571  ownerPaysTransferFee_ ? rate(book_.out.account) : QUALITY_ONE;
572 
574  maxOffersToConsume_, j_);
575 
577  sb, afView, book_, sb.parentCloseTime(), counter, j_);
578 
579  bool const flowCross = afView.rules().enabled(featureFlowCross);
580  bool offerAttempted = false;
582  while (offers.step())
583  {
584  auto& offer = offers.tip();
585 
586  // Note that offer.quality() returns a (non-optional) Quality. So
587  // ofrQ is always safe to use below this point in the loop.
588  if (!ofrQ)
589  ofrQ = offer.quality();
590  else if (*ofrQ != offer.quality())
591  break;
592 
593  if (static_cast<TDerived const*>(this)->limitSelfCrossQuality(
594  strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
595  continue;
596 
597  // Make sure offer owner has authorization to own IOUs from issuer.
598  // An account can always own XRP or their own IOUs.
599  if (flowCross && (!isXRP(offer.issueIn().currency)) &&
600  (offer.owner() != offer.issueIn().account))
601  {
602  auto const& issuerID = offer.issueIn().account;
603  auto const issuer = afView.read(keylet::account(issuerID));
604  if (issuer && ((*issuer)[sfFlags] & lsfRequireAuth))
605  {
606  // Issuer requires authorization. See if offer owner has that.
607  auto const& ownerID = offer.owner();
608  auto const authFlag =
609  issuerID > ownerID ? lsfHighAuth : lsfLowAuth;
610 
611  auto const line = afView.read(
612  keylet::line(ownerID, issuerID, offer.issueIn().currency));
613 
614  if (!line || (((*line)[sfFlags] & authFlag) == 0))
615  {
616  // Offer owner not authorized to hold IOU from issuer.
617  // Remove this offer even if no crossing occurs.
618  offers.permRmOffer(offer.key());
619  if (!offerAttempted)
620  // Change quality only if no previous offers were tried.
621  ofrQ = std::nullopt;
622  // This continue causes offers.step() to delete the offer.
623  continue;
624  }
625  }
626  }
627 
628  if (!static_cast<TDerived const*>(this)->checkQualityThreshold(offer))
629  break;
630 
631  auto const ofrInRate = static_cast<TDerived const*>(this)->getOfrInRate(
632  prevStep_, offer, trIn);
633 
634  auto const ofrOutRate =
635  static_cast<TDerived const*>(this)->getOfrOutRate(
636  prevStep_, offer, strandDst_, trOut);
637 
638  auto ofrAmt = offer.amount();
639  TAmounts stpAmt{
640  mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE, /*roundUp*/ true),
641  ofrAmt.out};
642 
643  // owner pays the transfer fee.
644  auto ownerGives =
645  mulRatio(ofrAmt.out, ofrOutRate, QUALITY_ONE, /*roundUp*/ false);
646 
647  auto const funds = (offer.owner() == offer.issueOut().account)
648  ? ownerGives // Offer owner is issuer; they have unlimited funds
649  : offers.ownerFunds();
650 
651  if (funds < ownerGives)
652  {
653  // We already know offer.owner()!=offer.issueOut().account
654  ownerGives = funds;
655  stpAmt.out = mulRatio(
656  ownerGives, QUALITY_ONE, ofrOutRate, /*roundUp*/ false);
657  ofrAmt = ofrQ->ceil_out(ofrAmt, stpAmt.out);
658  stpAmt.in =
659  mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE, /*roundUp*/ true);
660  }
661 
662  offerAttempted = true;
663  if (!callback(offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate))
664  break;
665  }
666 
667  return {offers.permToRemove(), counter.count()};
668 }
669 
670 template <class TIn, class TOut, class TDerived>
671 void
673  PaymentSandbox& sb,
674  TOffer<TIn, TOut>& offer,
675  TAmounts<TIn, TOut> const& ofrAmt,
676  TAmounts<TIn, TOut> const& stepAmt,
677  TOut const& ownerGives) const
678 {
679  // The offer owner gets the ofrAmt. The difference between ofrAmt and
680  // stepAmt is a transfer fee that goes to book_.in.account
681  {
682  auto const dr = accountSend(
683  sb,
684  book_.in.account,
685  offer.owner(),
686  toSTAmount(ofrAmt.in, book_.in),
687  j_);
688  if (dr != tesSUCCESS)
689  Throw<FlowException>(dr);
690  }
691 
692  // The offer owner pays `ownerGives`. The difference between ownerGives and
693  // stepAmt is a transfer fee that goes to book_.out.account
694  {
695  auto const cr = accountSend(
696  sb,
697  offer.owner(),
698  book_.out.account,
699  toSTAmount(ownerGives, book_.out),
700  j_);
701  if (cr != tesSUCCESS)
702  Throw<FlowException>(cr);
703  }
704 
705  offer.consume(sb, ofrAmt);
706 }
707 
708 template <class TCollection>
709 static auto
710 sum(TCollection const& col)
711 {
712  using TResult = std::decay_t<decltype(*col.begin())>;
713  if (col.empty())
714  return TResult{beast::zero};
715  return std::accumulate(col.begin() + 1, col.end(), *col.begin());
716 };
717 
718 template <class TIn, class TOut, class TDerived>
721  PaymentSandbox& sb,
722  ApplyView& afView,
723  boost::container::flat_set<uint256>& ofrsToRm,
724  TOut const& out)
725 {
726  cache_.reset();
727 
728  TAmounts<TIn, TOut> result(beast::zero, beast::zero);
729 
730  auto remainingOut = out;
731 
732  boost::container::flat_multiset<TIn> savedIns;
733  savedIns.reserve(64);
734  boost::container::flat_multiset<TOut> savedOuts;
735  savedOuts.reserve(64);
736 
737  /* amt fed will be adjusted by owner funds (and may differ from the offer's
738  amounts - tho always <=)
739  Return true to continue to receive offers, false to stop receiving offers.
740  */
741  auto eachOffer = [&](TOffer<TIn, TOut>& offer,
742  TAmounts<TIn, TOut> const& ofrAmt,
743  TAmounts<TIn, TOut> const& stpAmt,
744  TOut const& ownerGives,
745  std::uint32_t transferRateIn,
746  std::uint32_t transferRateOut) mutable -> bool {
747  if (remainingOut <= beast::zero)
748  return false;
749 
750  if (stpAmt.out <= remainingOut)
751  {
752  savedIns.insert(stpAmt.in);
753  savedOuts.insert(stpAmt.out);
754  result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
755  remainingOut = out - result.out;
756  this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
757  // return true b/c even if the payment is satisfied,
758  // we need to consume the offer
759  return true;
760  }
761  else
762  {
763  auto ofrAdjAmt = ofrAmt;
764  auto stpAdjAmt = stpAmt;
765  auto ownerGivesAdj = ownerGives;
766  limitStepOut(
767  offer.quality(),
768  ofrAdjAmt,
769  stpAdjAmt,
770  ownerGivesAdj,
771  transferRateIn,
772  transferRateOut,
773  remainingOut);
774  remainingOut = beast::zero;
775  savedIns.insert(stpAdjAmt.in);
776  savedOuts.insert(remainingOut);
777  result.in = sum(savedIns);
778  result.out = out;
779  this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
780 
781  // Explicitly check whether the offer is funded. Given that we have
782  // (stpAmt.out > remainingOut), it's natural to assume the offer
783  // will still be funded after consuming remainingOut but that is
784  // not always the case. If the mantissas of two IOU amounts differ
785  // by less than ten, then subtracting them leaves a zero.
786  return offer.fully_consumed();
787  }
788  };
789 
790  {
791  auto const prevStepDebtDir = [&] {
792  if (prevStep_)
793  return prevStep_->debtDirection(sb, StrandDirection::reverse);
794  return DebtDirection::issues;
795  }();
796  auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
797  boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
798  std::uint32_t const offersConsumed = std::get<1>(r);
799  offersUsed_ = offersConsumed;
800  SetUnion(ofrsToRm, toRm);
801 
802  if (offersConsumed >= maxOffersToConsume_)
803  {
804  // Too many iterations, mark this strand as inactive
805  if (!afView.rules().enabled(fix1515))
806  {
807  // Don't use the liquidity
808  cache_.emplace(beast::zero, beast::zero);
809  return {beast::zero, beast::zero};
810  }
811 
812  // Use the liquidity, but use this to mark the strand as inactive so
813  // it's not used further
814  inactive_ = true;
815  }
816  }
817 
818  switch (remainingOut.signum())
819  {
820  case -1: {
821  // something went very wrong
822  JLOG(j_.error())
823  << "BookStep remainingOut < 0 " << to_string(remainingOut);
824  assert(0);
825  cache_.emplace(beast::zero, beast::zero);
826  return {beast::zero, beast::zero};
827  }
828  case 0: {
829  // due to normalization, remainingOut can be zero without
830  // result.out == out. Force result.out == out for this case
831  result.out = out;
832  }
833  }
834 
835  cache_.emplace(result.in, result.out);
836  return {result.in, result.out};
837 }
838 
839 template <class TIn, class TOut, class TDerived>
842  PaymentSandbox& sb,
843  ApplyView& afView,
844  boost::container::flat_set<uint256>& ofrsToRm,
845  TIn const& in)
846 {
847  assert(cache_);
848 
849  TAmounts<TIn, TOut> result(beast::zero, beast::zero);
850 
851  auto remainingIn = in;
852 
853  boost::container::flat_multiset<TIn> savedIns;
854  savedIns.reserve(64);
855  boost::container::flat_multiset<TOut> savedOuts;
856  savedOuts.reserve(64);
857 
858  // amt fed will be adjusted by owner funds (and may differ from the offer's
859  // amounts - tho always <=)
860  auto eachOffer = [&](TOffer<TIn, TOut>& offer,
861  TAmounts<TIn, TOut> const& ofrAmt,
862  TAmounts<TIn, TOut> const& stpAmt,
863  TOut const& ownerGives,
864  std::uint32_t transferRateIn,
865  std::uint32_t transferRateOut) mutable -> bool {
866  assert(cache_);
867 
868  if (remainingIn <= beast::zero)
869  return false;
870 
871  bool processMore = true;
872  auto ofrAdjAmt = ofrAmt;
873  auto stpAdjAmt = stpAmt;
874  auto ownerGivesAdj = ownerGives;
875 
876  typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
877  if (stpAmt.in <= remainingIn)
878  {
879  savedIns.insert(stpAmt.in);
880  lastOut = savedOuts.insert(stpAmt.out);
881  result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
882  // consume the offer even if stepAmt.in == remainingIn
883  processMore = true;
884  }
885  else
886  {
887  limitStepIn(
888  offer.quality(),
889  ofrAdjAmt,
890  stpAdjAmt,
891  ownerGivesAdj,
892  transferRateIn,
893  transferRateOut,
894  remainingIn);
895  savedIns.insert(remainingIn);
896  lastOut = savedOuts.insert(stpAdjAmt.out);
897  result.out = sum(savedOuts);
898  result.in = in;
899 
900  processMore = false;
901  }
902 
903  if (result.out > cache_->out && result.in <= cache_->in)
904  {
905  // The step produced more output in the forward pass than the
906  // reverse pass while consuming the same input (or less). If we
907  // compute the input required to produce the cached output
908  // (produced in the reverse step) and the input is equal to
909  // the input consumed in the forward step, then consume the
910  // input provided in the forward step and produce the output
911  // requested from the reverse step.
912  auto const lastOutAmt = *lastOut;
913  savedOuts.erase(lastOut);
914  auto const remainingOut = cache_->out - sum(savedOuts);
915  auto ofrAdjAmtRev = ofrAmt;
916  auto stpAdjAmtRev = stpAmt;
917  auto ownerGivesAdjRev = ownerGives;
918  limitStepOut(
919  offer.quality(),
920  ofrAdjAmtRev,
921  stpAdjAmtRev,
922  ownerGivesAdjRev,
923  transferRateIn,
924  transferRateOut,
925  remainingOut);
926 
927  if (stpAdjAmtRev.in == remainingIn)
928  {
929  result.in = in;
930  result.out = cache_->out;
931 
932  savedIns.clear();
933  savedIns.insert(result.in);
934  savedOuts.clear();
935  savedOuts.insert(result.out);
936 
937  ofrAdjAmt = ofrAdjAmtRev;
938  stpAdjAmt.in = remainingIn;
939  stpAdjAmt.out = remainingOut;
940  ownerGivesAdj = ownerGivesAdjRev;
941  }
942  else
943  {
944  // This is (likely) a problem case, and wil be caught
945  // with later checks
946  savedOuts.insert(lastOutAmt);
947  }
948  }
949 
950  remainingIn = in - result.in;
951  this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
952 
953  // When the mantissas of two iou amounts differ by less than ten, then
954  // subtracting them leaves a result of zero. This can cause the check
955  // for (stpAmt.in > remainingIn) to incorrectly think an offer will be
956  // funded after subtracting remainingIn.
957  return processMore || offer.fully_consumed();
958  };
959 
960  {
961  auto const prevStepDebtDir = [&] {
962  if (prevStep_)
963  return prevStep_->debtDirection(sb, StrandDirection::forward);
964  return DebtDirection::issues;
965  }();
966  auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
967  boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
968  std::uint32_t const offersConsumed = std::get<1>(r);
969  offersUsed_ = offersConsumed;
970  SetUnion(ofrsToRm, toRm);
971 
972  if (offersConsumed >= maxOffersToConsume_)
973  {
974  // Too many iterations, mark this strand as inactive (dry)
975  if (!afView.rules().enabled(fix1515))
976  {
977  // Don't use the liquidity
978  cache_.emplace(beast::zero, beast::zero);
979  return {beast::zero, beast::zero};
980  }
981 
982  // Use the liquidity, but use this to mark the strand as inactive so
983  // it's not used further
984  inactive_ = true;
985  }
986  }
987 
988  switch (remainingIn.signum())
989  {
990  case -1: {
991  // something went very wrong
992  JLOG(j_.error())
993  << "BookStep remainingIn < 0 " << to_string(remainingIn);
994  assert(0);
995  cache_.emplace(beast::zero, beast::zero);
996  return {beast::zero, beast::zero};
997  }
998  case 0: {
999  // due to normalization, remainingIn can be zero without
1000  // result.in == in. Force result.in == in for this case
1001  result.in = in;
1002  }
1003  }
1004 
1005  cache_.emplace(result.in, result.out);
1006  return {result.in, result.out};
1007 }
1008 
1009 template <class TIn, class TOut, class TDerived>
1012  PaymentSandbox& sb,
1013  ApplyView& afView,
1014  EitherAmount const& in)
1015 {
1016  if (!cache_)
1017  {
1018  JLOG(j_.trace()) << "Expected valid cache in validFwd";
1019  return {false, EitherAmount(TOut(beast::zero))};
1020  }
1021 
1022  auto const savCache = *cache_;
1023 
1024  try
1025  {
1026  boost::container::flat_set<uint256> dummy;
1027  fwdImp(sb, afView, dummy, get<TIn>(in)); // changes cache
1028  }
1029  catch (FlowException const&)
1030  {
1031  return {false, EitherAmount(TOut(beast::zero))};
1032  }
1033 
1034  if (!(checkNear(savCache.in, cache_->in) &&
1035  checkNear(savCache.out, cache_->out)))
1036  {
1037  JLOG(j_.warn()) << "Strand re-execute check failed."
1038  << " ExpectedIn: " << to_string(savCache.in)
1039  << " CachedIn: " << to_string(cache_->in)
1040  << " ExpectedOut: " << to_string(savCache.out)
1041  << " CachedOut: " << to_string(cache_->out);
1042  return {false, EitherAmount(cache_->out)};
1043  }
1044  return {true, EitherAmount(cache_->out)};
1045 }
1046 
1047 template <class TIn, class TOut, class TDerived>
1048 TER
1050 {
1051  if (book_.in == book_.out)
1052  {
1053  JLOG(j_.debug()) << "BookStep: Book with same in and out issuer "
1054  << *this;
1055  return temBAD_PATH;
1056  }
1057  if (!isConsistent(book_.in) || !isConsistent(book_.out))
1058  {
1059  JLOG(j_.debug()) << "Book: currency is inconsistent with issuer."
1060  << *this;
1061  return temBAD_PATH;
1062  }
1063 
1064  // Do not allow two books to output the same issue. This may cause offers on
1065  // one step to unfund offers in another step.
1066  if (!ctx.seenBookOuts.insert(book_.out).second ||
1067  ctx.seenDirectIssues[0].count(book_.out))
1068  {
1069  JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
1070  return temBAD_PATH_LOOP;
1071  }
1072 
1073  if (ctx.seenDirectIssues[1].count(book_.out))
1074  {
1075  JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
1076  return temBAD_PATH_LOOP;
1077  }
1078 
1079  auto issuerExists = [](ReadView const& view, Issue const& iss) -> bool {
1080  return isXRP(iss.account) || view.read(keylet::account(iss.account));
1081  };
1082 
1083  if (!issuerExists(ctx.view, book_.in) || !issuerExists(ctx.view, book_.out))
1084  {
1085  JLOG(j_.debug()) << "BookStep: deleted issuer detected: " << *this;
1086  return tecNO_ISSUER;
1087  }
1088 
1089  if (ctx.prevStep)
1090  {
1091  if (auto const prev = ctx.prevStep->directStepSrcAcct())
1092  {
1093  auto const& view = ctx.view;
1094  auto const& cur = book_.in.account;
1095 
1096  auto sle = view.read(keylet::line(*prev, cur, book_.in.currency));
1097  if (!sle)
1098  return terNO_LINE;
1099  if ((*sle)[sfFlags] &
1100  ((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple))
1101  return terNO_RIPPLE;
1102  }
1103  }
1104 
1105  return tesSUCCESS;
1106 }
1107 
1108 //------------------------------------------------------------------------------
1109 
1110 namespace test {
1111 // Needed for testing
1112 
1113 template <class TIn, class TOut, class TDerived>
1114 static bool
1115 equalHelper(Step const& step, ripple::Book const& book)
1116 {
1117  if (auto bs = dynamic_cast<BookStep<TIn, TOut, TDerived> const*>(&step))
1118  return book == bs->book();
1119  return false;
1120 }
1121 
1122 bool
1123 bookStepEqual(Step const& step, ripple::Book const& book)
1124 {
1125  bool const inXRP = isXRP(book.in.currency);
1126  bool const outXRP = isXRP(book.out.currency);
1127  if (inXRP && outXRP)
1128  {
1129  assert(0);
1130  return false; // no such thing as xrp/xrp book step
1131  }
1132  if (inXRP && !outXRP)
1133  return equalHelper<
1134  XRPAmount,
1135  IOUAmount,
1137  if (!inXRP && outXRP)
1138  return equalHelper<
1139  IOUAmount,
1140  XRPAmount,
1142  if (!inXRP && !outXRP)
1143  return equalHelper<
1144  IOUAmount,
1145  IOUAmount,
1147  return false;
1148 }
1149 } // namespace test
1150 
1151 //------------------------------------------------------------------------------
1152 
1153 template <class TIn, class TOut>
1155 make_BookStepHelper(StrandContext const& ctx, Issue const& in, Issue const& out)
1156 {
1157  TER ter = tefINTERNAL;
1159  if (ctx.offerCrossing)
1160  {
1161  auto offerCrossingStep =
1162  std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx, in, out);
1163  ter = offerCrossingStep->check(ctx);
1164  r = std::move(offerCrossingStep);
1165  }
1166  else // payment
1167  {
1168  auto paymentStep =
1169  std::make_unique<BookPaymentStep<TIn, TOut>>(ctx, in, out);
1170  ter = paymentStep->check(ctx);
1171  r = std::move(paymentStep);
1172  }
1173  if (ter != tesSUCCESS)
1174  return {ter, nullptr};
1175 
1176  return {tesSUCCESS, std::move(r)};
1177 }
1178 
1180 make_BookStepII(StrandContext const& ctx, Issue const& in, Issue const& out)
1181 {
1182  return make_BookStepHelper<IOUAmount, IOUAmount>(ctx, in, out);
1183 }
1184 
1186 make_BookStepIX(StrandContext const& ctx, Issue const& in)
1187 {
1188  return make_BookStepHelper<IOUAmount, XRPAmount>(ctx, in, xrpIssue());
1189 }
1190 
1192 make_BookStepXI(StrandContext const& ctx, Issue const& out)
1193 {
1194  return make_BookStepHelper<XRPAmount, IOUAmount>(ctx, xrpIssue(), out);
1195 }
1196 
1197 } // 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:101
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)
Definition: BookStep.cpp:527
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:550
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:1192
ripple::make_BookStepII
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1180
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:1115
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:254
ripple::lsfLowNoRipple
@ lsfLowNoRipple
Definition: LedgerFormats.h:256
ripple::BookPaymentStep::BookPaymentStep
BookPaymentStep()=default
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:1011
ripple::test::bookStepEqual
bool bookStepEqual(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1123
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:255
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:672
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:841
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:1049
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:257
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:1155
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::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:720
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:1186
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:710
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::BookOfferCrossingStep::adjustQualityWithFees
Quality adjustQualityWithFees(ReadView const &v, Quality const &ofrQ, DebtDirection prevStepDir) const
Definition: BookStep.cpp:438