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