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