rippled
Loading...
Searching...
No Matches
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 <xrpld/app/misc/AMMUtils.h>
21#include <xrpld/app/paths/AMMLiquidity.h>
22#include <xrpld/app/paths/AMMOffer.h>
23#include <xrpld/app/paths/detail/FlatSets.h>
24#include <xrpld/app/paths/detail/Steps.h>
25#include <xrpld/app/tx/detail/OfferStream.h>
26#include <xrpld/ledger/PaymentSandbox.h>
27#include <xrpl/basics/Log.h>
28#include <xrpl/basics/contract.h>
29#include <xrpl/beast/utility/instrumentation.h>
30#include <xrpl/protocol/Book.h>
31#include <xrpl/protocol/Feature.h>
32#include <xrpl/protocol/IOUAmount.h>
33#include <xrpl/protocol/Quality.h>
34#include <xrpl/protocol/XRPAmount.h>
35
36#include <boost/container/flat_set.hpp>
37
38#include <numeric>
39#include <sstream>
40
41namespace ripple {
42
43template <class TIn, class TOut, class TDerived>
44class BookStep : public StepImp<TIn, TOut, BookStep<TIn, TOut, TDerived>>
45{
46protected:
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
92public:
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
151 const override;
152
154 getQualityFunc(ReadView const& v, DebtDirection prevStepDir) const override;
155
157 offersUsed() const override;
158
161 PaymentSandbox& sb,
162 ApplyView& afView,
163 boost::container::flat_set<uint256>& ofrsToRm,
164 TOut const& out);
165
168 PaymentSandbox& sb,
169 ApplyView& afView,
170 boost::container::flat_set<uint256>& ofrsToRm,
171 TIn const& in);
172
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
187protected:
189 logStringImpl(char const* name) const
190 {
192 ostr << name << ": " << "\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
199private:
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>
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
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).
269template <class TIn, class TOut>
270class BookPaymentStep : public BookStep<TIn, TOut, BookPaymentStep<TIn, TOut>>
271{
272public:
273 explicit BookPaymentStep() = default;
274
275 using BookStep<TIn, TOut, BookPaymentStep<TIn, TOut>>::BookStep;
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 // A payment doesn't use quality threshold (limitQuality)
301 // since the strand's quality doesn't directly relate to the step's quality.
303 qualityThreshold(Quality const& lobQuality) const
304 {
305 return lobQuality;
306 }
307
308 // For a payment ofrInRate is always the same as trIn.
310 getOfrInRate(Step const*, AccountID const&, std::uint32_t trIn) const
311 {
312 return trIn;
313 }
314
315 // For a payment ofrOutRate is always the same as trOut.
318 Step const*,
319 AccountID const&,
320 AccountID const&,
321 std::uint32_t trOut) const
322 {
323 return trOut;
324 }
325
326 Quality
328 ReadView const& v,
329 Quality const& ofrQ,
330 DebtDirection prevStepDir,
331 WaiveTransferFee waiveFee,
332 OfferType,
333 Rules const&) const
334 {
335 // Charge the offer owner, not the sender
336 // Charge a fee even if the owner is the same as the issuer
337 // (the old code does not charge a fee)
338 // Calculate amount that goes to the taker and the amount charged the
339 // offer owner
340 auto rate = [&](AccountID const& id) {
341 if (isXRP(id) || id == this->strandDst_)
342 return parityRate;
343 return transferRate(v, id);
344 };
345
346 auto const trIn =
347 redeems(prevStepDir) ? rate(this->book_.in.account) : parityRate;
348 // Always charge the transfer fee, even if the owner is the issuer,
349 // unless the fee is waived
350 auto const trOut =
351 (this->ownerPaysTransferFee_ && waiveFee == WaiveTransferFee::No)
352 ? rate(this->book_.out.account)
353 : parityRate;
354
355 Quality const q1{getRate(STAmount(trOut.value), STAmount(trIn.value))};
356 return composed_quality(q1, ofrQ);
357 }
358
360 logString() const override
361 {
362 return this->logStringImpl("BookPaymentStep");
363 }
364};
365
366// Offer crossing BookStep template class (not a payment).
367template <class TIn, class TOut>
369 : public BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>
370{
375
376private:
377 // Helper function that throws if the optional passed to the constructor
378 // is none.
379 static Quality
381 {
382 // It's really a programming error if the quality is missing.
383 XRPL_ASSERT(
384 limitQuality,
385 "ripple::BookOfferCrossingStep::getQuality : nonzero quality");
386 if (!limitQuality)
387 Throw<FlowException>(tefINTERNAL, "Offer requires quality.");
388 return *limitQuality;
389 }
390
391public:
393 StrandContext const& ctx,
394 Issue const& in,
395 Issue const& out)
396 : BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>(ctx, in, out)
398 , qualityThreshold_(getQuality(ctx.limitQuality))
399 {
400 }
401
402 template <template <typename, typename> typename Offer>
403 bool
405 AccountID const& strandSrc,
406 AccountID const& strandDst,
407 Offer<TIn, TOut> const& offer,
410 bool const offerAttempted) const
411 {
412 // This method supports some correct but slightly surprising
413 // behavior in offer crossing. The scenario:
414 //
415 // o alice has already created one or more offers.
416 // o alice creates another offer that can be directly crossed (not
417 // autobridged) by one or more of her previously created offer(s).
418 //
419 // What does the offer crossing do?
420 //
421 // o The offer crossing could go ahead and cross the offers leaving
422 // either one reduced offer (partial crossing) or zero offers
423 // (exact crossing) in the ledger. We don't do this. And, really,
424 // the offer creator probably didn't want us to.
425 //
426 // o We could skip over the self offer in the book and only cross
427 // offers that are not our own. This would make a lot of sense,
428 // but we don't do it. Part of the rationale is that we can only
429 // operate on the tip of the order book. We can't leave an offer
430 // behind -- it would sit on the tip and block access to other
431 // offers.
432 //
433 // o We could delete the self-crossable offer(s) off the tip of the
434 // book and continue with offer crossing. That's what we do.
435 //
436 // To support this scenario offer crossing has a special rule. If:
437 // a. We're offer crossing using default path (no autobridging), and
438 // b. The offer's quality is at least as good as our quality, and
439 // c. We're about to cross one of our own offers, then
440 // d. Delete the old offer from the ledger.
441 if (defaultPath_ && offer.quality() >= qualityThreshold_ &&
442 strandSrc == offer.owner() && strandDst == offer.owner())
443 {
444 // Remove this offer even if no crossing occurs.
445 if (auto const key = offer.key())
446 offers.permRmOffer(*key);
447
448 // If no offers have been attempted yet then it's okay to move to
449 // a different quality.
450 if (!offerAttempted)
451 ofrQ = std::nullopt;
452
453 // Return true so the current offer will be deleted.
454 return true;
455 }
456 return false;
457 }
458
459 // Offer crossing can prune the offers it needs to look at with a
460 // quality threshold.
461 bool
462 checkQualityThreshold(Quality const& quality) const
463 {
464 return !defaultPath_ || quality >= qualityThreshold_;
465 }
466
467 // Return quality threshold or nullopt to use when generating AMM offer.
468 // AMM synthetic offer is generated to match LOB offer quality.
469 // If LOB tip offer quality is less than qualityThreshold
470 // then generated AMM offer quality is also less than qualityThreshold and
471 // the offer is not crossed even though AMM might generate a better quality
472 // offer. To address this, if qualityThreshold is greater than lobQuality
473 // then don't use quality to generate the AMM offer. The limit out value
474 // generates the maximum AMM offer in this case, which matches
475 // the quality threshold. This only applies to single path scenario.
476 // Multi-path AMM offers work the same as LOB offers.
478 qualityThreshold(Quality const& lobQuality) const
479 {
480 if (this->ammLiquidity_ && !this->ammLiquidity_->multiPath() &&
481 qualityThreshold_ > lobQuality)
482 return std::nullopt;
483 return lobQuality;
484 }
485
486 // For offer crossing don't pay the transfer fee if alice is paying alice.
487 // A regular (non-offer-crossing) payment does not apply this rule.
490 Step const* prevStep,
491 AccountID const& owner,
492 std::uint32_t trIn) const
493 {
494 auto const srcAcct =
495 prevStep ? prevStep->directStepSrcAcct() : std::nullopt;
496
497 return owner == srcAcct // If offer crossing && prevStep is DirectI
498 ? QUALITY_ONE // && src is offer owner
499 : trIn; // then rate = QUALITY_ONE
500 }
501
502 // See comment on getOfrInRate().
505 Step const* prevStep,
506 AccountID const& owner,
507 AccountID const& strandDst,
508 std::uint32_t trOut) const
509 {
510 return // If offer crossing
511 prevStep && prevStep->bookStepBook() && // && prevStep is BookStep
512 owner == strandDst // && dest is offer owner
513 ? QUALITY_ONE
514 : trOut; // then rate = QUALITY_ONE
515 }
516
517 Quality
519 ReadView const& v,
520 Quality const& ofrQ,
521 DebtDirection prevStepDir,
522 WaiveTransferFee waiveFee,
523 OfferType offerType,
524 Rules const& rules) const
525 {
526 // Offer x-ing does not charge a transfer fee when the offer's owner
527 // is the same as the strand dst. It is important that
528 // `qualityUpperBound` is an upper bound on the quality (it is used to
529 // ignore strands whose quality cannot meet a minimum threshold). When
530 // calculating quality assume no fee is charged, or the estimate will no
531 // longer be an upper bound.
532
533 // Single path AMM offer has to factor in the transfer in rate
534 // when calculating the upper bound quality and the quality function
535 // because single path AMM's offer quality is not constant.
536 if (!rules.enabled(fixAMMv1_1))
537 return ofrQ;
538 else if (
539 offerType == OfferType::CLOB ||
540 (this->ammLiquidity_ && this->ammLiquidity_->multiPath()))
541 return ofrQ;
542
543 auto rate = [&](AccountID const& id) {
544 if (isXRP(id) || id == this->strandDst_)
545 return parityRate;
546 return transferRate(v, id);
547 };
548
549 auto const trIn =
550 redeems(prevStepDir) ? rate(this->book_.in.account) : parityRate;
551 // AMM doesn't pay the transfer fee on the out amount
552 auto const trOut = parityRate;
553
554 Quality const q1{getRate(STAmount(trOut.value), STAmount(trIn.value))};
555 return composed_quality(q1, ofrQ);
556 }
557
559 logString() const override
560 {
561 return this->logStringImpl("BookOfferCrossingStep");
562 }
563
564private:
565 bool const defaultPath_;
566 Quality const qualityThreshold_;
567};
568
569//------------------------------------------------------------------------------
570
571template <class TIn, class TOut, class TDerived>
572bool
574{
575 if (auto bs = dynamic_cast<BookStep<TIn, TOut, TDerived> const*>(&rhs))
576 return book_ == bs->book_;
577 return false;
578}
579
580template <class TIn, class TOut, class TDerived>
583 ReadView const& v,
584 DebtDirection prevStepDir) const
585{
586 auto const dir = this->debtDirection(v, StrandDirection::forward);
587
588 std::optional<std::pair<Quality, OfferType>> const res = tipOfferQuality(v);
589 if (!res)
590 return {std::nullopt, dir};
591
592 auto const waiveFee = (std::get<OfferType>(*res) == OfferType::AMM)
595
596 Quality const q = static_cast<TDerived const*>(this)->adjustQualityWithFees(
597 v,
598 std::get<Quality>(*res),
599 prevStepDir,
600 waiveFee,
601 std::get<OfferType>(*res),
602 v.rules());
603 return {q, dir};
604}
605
606template <class TIn, class TOut, class TDerived>
609 ReadView const& v,
610 DebtDirection prevStepDir) const
611{
612 auto const dir = this->debtDirection(v, StrandDirection::forward);
613
614 std::optional<QualityFunction> const res = tipOfferQualityF(v);
615 if (!res)
616 return {std::nullopt, dir};
617
618 // AMM
619 if (!res->isConst())
620 {
621 auto static const qOne = Quality{STAmount::uRateOne};
622 auto const q =
623 static_cast<TDerived const*>(this)->adjustQualityWithFees(
624 v,
625 qOne,
626 prevStepDir,
628 OfferType::AMM,
629 v.rules());
630 if (q == qOne)
631 return {res, dir};
633 qf.combine(*res);
634 return {qf, dir};
635 }
636
637 // CLOB
638 Quality const q = static_cast<TDerived const*>(this)->adjustQualityWithFees(
639 v,
640 *(res->quality()),
641 prevStepDir,
643 OfferType::CLOB,
644 v.rules());
646}
647
648template <class TIn, class TOut, class TDerived>
651{
652 return offersUsed_;
653}
654
655// Adjust the offer amount and step amount subject to the given input limit
656template <class TIn, class TOut, class Offer>
657static void
659 Offer const& offer,
660 TAmounts<TIn, TOut>& ofrAmt,
661 TAmounts<TIn, TOut>& stpAmt,
662 TOut& ownerGives,
663 std::uint32_t transferRateIn,
664 std::uint32_t transferRateOut,
665 TIn const& limit)
666{
667 if (limit < stpAmt.in)
668 {
669 stpAmt.in = limit;
670 auto const inLmt =
671 mulRatio(stpAmt.in, QUALITY_ONE, transferRateIn, /*roundUp*/ false);
672 // It turns out we can prevent order book blocking by (strictly)
673 // rounding down the ceil_in() result. By rounding down we guarantee
674 // that the quality of an offer left in the ledger is as good or
675 // better than the quality of the containing order book page.
676 //
677 // This adjustment changes transaction outcomes, so it must be made
678 // under an amendment.
679 ofrAmt = offer.limitIn(ofrAmt, inLmt, /* roundUp */ false);
680 stpAmt.out = ofrAmt.out;
681 ownerGives = mulRatio(
682 ofrAmt.out, transferRateOut, QUALITY_ONE, /*roundUp*/ false);
683 }
684}
685
686// Adjust the offer amount and step amount subject to the given output limit
687template <class TIn, class TOut, class Offer>
688static void
690 Offer const& offer,
691 TAmounts<TIn, TOut>& ofrAmt,
692 TAmounts<TIn, TOut>& stpAmt,
693 TOut& ownerGives,
694 std::uint32_t transferRateIn,
695 std::uint32_t transferRateOut,
696 TOut const& limit)
697{
698 if (limit < stpAmt.out)
699 {
700 stpAmt.out = limit;
701 ownerGives = mulRatio(
702 stpAmt.out, transferRateOut, QUALITY_ONE, /*roundUp*/ false);
703 ofrAmt = offer.limitOut(
704 ofrAmt,
705 stpAmt.out,
706 /*roundUp*/ true);
707 stpAmt.in =
708 mulRatio(ofrAmt.in, transferRateIn, QUALITY_ONE, /*roundUp*/ true);
709 }
710}
711
712template <class TIn, class TOut, class TDerived>
713template <class Callback>
716 PaymentSandbox& sb,
717 ApplyView& afView,
718 DebtDirection prevStepDir,
719 Callback& callback) const
720{
721 // Charge the offer owner, not the sender
722 // Charge a fee even if the owner is the same as the issuer
723 // (the old code does not charge a fee)
724 // Calculate amount that goes to the taker and the amount charged the offer
725 // owner
726 auto rate = [this, &sb](AccountID const& id) -> std::uint32_t {
727 if (isXRP(id) || id == this->strandDst_)
728 return QUALITY_ONE;
729 return transferRate(sb, id).value;
730 };
731
732 std::uint32_t const trIn =
733 redeems(prevStepDir) ? rate(book_.in.account) : QUALITY_ONE;
734 // Always charge the transfer fee, even if the owner is the issuer
735 std::uint32_t const trOut =
736 ownerPaysTransferFee_ ? rate(book_.out.account) : QUALITY_ONE;
737
739 maxOffersToConsume_, j_);
740
742 sb, afView, book_, sb.parentCloseTime(), counter, j_);
743
744 bool const flowCross = afView.rules().enabled(featureFlowCross);
745 bool offerAttempted = false;
747 auto execOffer = [&](auto& offer) {
748 // Note that offer.quality() returns a (non-optional) Quality. So
749 // ofrQ is always safe to use below this point in the lambda.
750 if (!ofrQ)
751 ofrQ = offer.quality();
752 else if (*ofrQ != offer.quality())
753 return false;
754
755 if (static_cast<TDerived const*>(this)->limitSelfCrossQuality(
756 strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
757 return true;
758
759 // Make sure offer owner has authorization to own IOUs from issuer.
760 // An account can always own XRP or their own IOUs.
761 if (flowCross && (!isXRP(offer.issueIn().currency)) &&
762 (offer.owner() != offer.issueIn().account))
763 {
764 auto const& issuerID = offer.issueIn().account;
765 auto const issuer = afView.read(keylet::account(issuerID));
766 if (issuer && ((*issuer)[sfFlags] & lsfRequireAuth))
767 {
768 // Issuer requires authorization. See if offer owner has that.
769 auto const& ownerID = offer.owner();
770 auto const authFlag =
771 issuerID > ownerID ? lsfHighAuth : lsfLowAuth;
772
773 auto const line = afView.read(
774 keylet::line(ownerID, issuerID, offer.issueIn().currency));
775
776 if (!line || (((*line)[sfFlags] & authFlag) == 0))
777 {
778 // Offer owner not authorized to hold IOU from issuer.
779 // Remove this offer even if no crossing occurs.
780 if (auto const key = offer.key())
781 offers.permRmOffer(*key);
782 if (!offerAttempted)
783 // Change quality only if no previous offers were tried.
784 ofrQ = std::nullopt;
785 // Returning true causes offers.step() to delete the offer.
786 return true;
787 }
788 }
789 }
790
791 if (!static_cast<TDerived const*>(this)->checkQualityThreshold(
792 offer.quality()))
793 return false;
794
795 auto const [ofrInRate, ofrOutRate] = offer.adjustRates(
796 static_cast<TDerived const*>(this)->getOfrInRate(
797 prevStep_, offer.owner(), trIn),
798 static_cast<TDerived const*>(this)->getOfrOutRate(
799 prevStep_, offer.owner(), strandDst_, trOut));
800
801 auto ofrAmt = offer.amount();
802 TAmounts stpAmt{
803 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE, /*roundUp*/ true),
804 ofrAmt.out};
805
806 // owner pays the transfer fee.
807 auto ownerGives =
808 mulRatio(ofrAmt.out, ofrOutRate, QUALITY_ONE, /*roundUp*/ false);
809
810 auto const funds = offer.isFunded()
811 ? ownerGives // Offer owner is issuer; they have unlimited funds
812 : offers.ownerFunds();
813
814 // Only if CLOB offer
815 if (funds < ownerGives)
816 {
817 // We already know offer.owner()!=offer.issueOut().account
818 ownerGives = funds;
819 stpAmt.out = mulRatio(
820 ownerGives, QUALITY_ONE, ofrOutRate, /*roundUp*/ false);
821
822 // It turns out we can prevent order book blocking by (strictly)
823 // rounding down the ceil_out() result. This adjustment changes
824 // transaction outcomes, so it must be made under an amendment.
825 ofrAmt = offer.limitOut(ofrAmt, stpAmt.out, /*roundUp*/ false);
826
827 stpAmt.in =
828 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE, /*roundUp*/ true);
829 }
830
831 offerAttempted = true;
832 return callback(
833 offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate);
834 };
835
836 // At any payment engine iteration, AMM offer can only be consumed once.
837 auto tryAMM = [&](std::optional<Quality> const& lobQuality) -> bool {
838 // If offer crossing then use either LOB quality or nullopt
839 // to prevent AMM being blocked by a lower quality LOB.
840 auto const qualityThreshold = [&]() -> std::optional<Quality> {
841 if (sb.rules().enabled(fixAMMv1_1) && lobQuality)
842 return static_cast<TDerived const*>(this)->qualityThreshold(
843 *lobQuality);
844 return lobQuality;
845 }();
846 auto ammOffer = getAMMOffer(sb, qualityThreshold);
847 return !ammOffer || execOffer(*ammOffer);
848 };
849
850 if (offers.step())
851 {
852 if (tryAMM(offers.tip().quality()))
853 {
854 do
855 {
856 if (!execOffer(offers.tip()))
857 break;
858 } while (offers.step());
859 }
860 }
861 else
862 {
863 // Might have AMM offer if there are no LOB offers.
864 tryAMM(std::nullopt);
865 }
866
867 return {offers.permToRemove(), counter.count()};
868}
869
870template <class TIn, class TOut, class TDerived>
871template <template <typename, typename> typename Offer>
872void
874 PaymentSandbox& sb,
875 Offer<TIn, TOut>& offer,
876 TAmounts<TIn, TOut> const& ofrAmt,
877 TAmounts<TIn, TOut> const& stepAmt,
878 TOut const& ownerGives) const
879{
880 if (!offer.checkInvariant(ofrAmt, j_))
881 {
882 // purposely written as separate if statements so we get logging even
883 // when the amendment isn't active.
884 if (sb.rules().enabled(fixAMMOverflowOffer))
885 {
886 Throw<FlowException>(
887 tecINVARIANT_FAILED, "AMM pool product invariant failed.");
888 }
889 }
890
891 // The offer owner gets the ofrAmt. The difference between ofrAmt and
892 // stepAmt is a transfer fee that goes to book_.in.account
893 {
894 auto const dr = offer.send(
895 sb,
896 book_.in.account,
897 offer.owner(),
898 toSTAmount(ofrAmt.in, book_.in),
899 j_);
900 if (dr != tesSUCCESS)
901 Throw<FlowException>(dr);
902 }
903
904 // The offer owner pays `ownerGives`. The difference between ownerGives and
905 // stepAmt is a transfer fee that goes to book_.out.account
906 {
907 auto const cr = offer.send(
908 sb,
909 offer.owner(),
910 book_.out.account,
911 toSTAmount(ownerGives, book_.out),
912 j_);
913 if (cr != tesSUCCESS)
914 Throw<FlowException>(cr);
915 }
916
917 offer.consume(sb, ofrAmt);
918}
919
920template <class TIn, class TOut, class TDerived>
923 ReadView const& view,
924 std::optional<Quality> const& clobQuality) const
925{
926 if (ammLiquidity_)
927 return ammLiquidity_->getOffer(view, clobQuality);
928 return std::nullopt;
929}
930
931template <class TIn, class TOut, class TDerived>
934{
935 // This can be simplified (and sped up) if directories are never empty.
936 Sandbox sb(&view, tapNONE);
937 BookTip bt(sb, book_);
938 auto const lobQuality =
939 bt.step(j_) ? std::optional<Quality>(bt.quality()) : std::nullopt;
940 // Multi-path offer generates an offer with the quality
941 // calculated from the offer size and the quality is constant in this case.
942 // Single path offer quality changes with the offer size. Spot price quality
943 // (SPQ) can't be used in this case as the upper bound quality because
944 // even if SPQ quality is better than LOB quality, it might not be possible
945 // to generate AMM offer at or better quality than LOB quality. Another
946 // factor to consider is limit quality on offer crossing. If LOB quality
947 // is greater than limit quality then use LOB quality when generating AMM
948 // offer, otherwise don't use quality threshold when generating AMM offer.
949 // AMM or LOB offer, whether multi-path or single path then can be selected
950 // based on the best offer quality. Using the quality to generate AMM offer
951 // in this case also prevents the payment engine from going into multiple
952 // iterations to cross a LOB offer. This happens when AMM changes
953 // the out amount at the start of iteration to match the limitQuality
954 // on offer crossing but AMM can't generate the offer at this quality,
955 // as the result a LOB offer is partially crossed, and it might take a few
956 // iterations to fully cross the offer.
957 auto const qualityThreshold = [&]() -> std::optional<Quality> {
958 if (view.rules().enabled(fixAMMv1_1) && lobQuality)
959 return static_cast<TDerived const*>(this)->qualityThreshold(
960 *lobQuality);
961 return std::nullopt;
962 }();
963 // AMM quality is better or no LOB offer
964 if (auto const ammOffer = getAMMOffer(view, qualityThreshold); ammOffer &&
965 ((lobQuality && ammOffer->quality() > lobQuality) || !lobQuality))
966 return ammOffer;
967 // LOB quality is better or nullopt
968 return lobQuality;
969}
970
971template <class TIn, class TOut, class TDerived>
972auto
975{
976 if (auto const res = tip(view); !res)
977 return std::nullopt;
978 else if (auto const q = std::get_if<Quality>(&(*res)))
979 return std::make_pair(*q, OfferType::CLOB);
980 else
981 return std::make_pair(
982 std::get<AMMOffer<TIn, TOut>>(*res).quality(), OfferType::AMM);
983}
984
985template <class TIn, class TOut, class TDerived>
988{
989 if (auto const res = tip(view); !res)
990 return std::nullopt;
991 else if (auto const q = std::get_if<Quality>(&(*res)))
993 else
994 return std::get<AMMOffer<TIn, TOut>>(*res).getQualityFunc();
995}
996
997template <class TCollection>
998static auto
999sum(TCollection const& col)
1000{
1001 using TResult = std::decay_t<decltype(*col.begin())>;
1002 if (col.empty())
1003 return TResult{beast::zero};
1004 return std::accumulate(col.begin() + 1, col.end(), *col.begin());
1005};
1006
1007template <class TIn, class TOut, class TDerived>
1010 PaymentSandbox& sb,
1011 ApplyView& afView,
1012 boost::container::flat_set<uint256>& ofrsToRm,
1013 TOut const& out)
1014{
1015 cache_.reset();
1016
1017 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
1018
1019 auto remainingOut = out;
1020
1021 boost::container::flat_multiset<TIn> savedIns;
1022 savedIns.reserve(64);
1023 boost::container::flat_multiset<TOut> savedOuts;
1024 savedOuts.reserve(64);
1025
1026 /* amt fed will be adjusted by owner funds (and may differ from the offer's
1027 amounts - tho always <=)
1028 Return true to continue to receive offers, false to stop receiving offers.
1029 */
1030 auto eachOffer = [&](auto& offer,
1031 TAmounts<TIn, TOut> const& ofrAmt,
1032 TAmounts<TIn, TOut> const& stpAmt,
1033 TOut const& ownerGives,
1034 std::uint32_t transferRateIn,
1035 std::uint32_t transferRateOut) mutable -> bool {
1036 if (remainingOut <= beast::zero)
1037 return false;
1038
1039 if (stpAmt.out <= remainingOut)
1040 {
1041 savedIns.insert(stpAmt.in);
1042 savedOuts.insert(stpAmt.out);
1043 result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
1044 remainingOut = out - result.out;
1045 this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
1046 // return true b/c even if the payment is satisfied,
1047 // we need to consume the offer
1048 return true;
1049 }
1050 else
1051 {
1052 auto ofrAdjAmt = ofrAmt;
1053 auto stpAdjAmt = stpAmt;
1054 auto ownerGivesAdj = ownerGives;
1056 offer,
1057 ofrAdjAmt,
1058 stpAdjAmt,
1059 ownerGivesAdj,
1060 transferRateIn,
1061 transferRateOut,
1062 remainingOut);
1063 remainingOut = beast::zero;
1064 savedIns.insert(stpAdjAmt.in);
1065 savedOuts.insert(remainingOut);
1066 result.in = sum(savedIns);
1067 result.out = out;
1068 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
1069
1070 // Explicitly check whether the offer is funded. Given that we have
1071 // (stpAmt.out > remainingOut), it's natural to assume the offer
1072 // will still be funded after consuming remainingOut but that is
1073 // not always the case. If the mantissas of two IOU amounts differ
1074 // by less than ten, then subtracting them leaves a zero.
1075 return offer.fully_consumed();
1076 }
1077 };
1078
1079 {
1080 auto const prevStepDebtDir = [&] {
1081 if (prevStep_)
1082 return prevStep_->debtDirection(sb, StrandDirection::reverse);
1083 return DebtDirection::issues;
1084 }();
1085 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
1086 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
1087 std::uint32_t const offersConsumed = std::get<1>(r);
1088 offersUsed_ = offersConsumed;
1089 SetUnion(ofrsToRm, toRm);
1090
1091 if (offersConsumed >= maxOffersToConsume_)
1092 {
1093 // Too many iterations, mark this strand as inactive
1094 if (!afView.rules().enabled(fix1515))
1095 {
1096 // Don't use the liquidity
1097 cache_.emplace(beast::zero, beast::zero);
1098 return {beast::zero, beast::zero};
1099 }
1100
1101 // Use the liquidity, but use this to mark the strand as inactive so
1102 // it's not used further
1103 inactive_ = true;
1104 }
1105 }
1106
1107 switch (remainingOut.signum())
1108 {
1109 case -1: {
1110 // something went very wrong
1111 JLOG(j_.error())
1112 << "BookStep remainingOut < 0 " << to_string(remainingOut);
1113 UNREACHABLE("ripple::BookStep::revImp : remaining less than zero");
1114 cache_.emplace(beast::zero, beast::zero);
1115 return {beast::zero, beast::zero};
1116 }
1117 case 0: {
1118 // due to normalization, remainingOut can be zero without
1119 // result.out == out. Force result.out == out for this case
1120 result.out = out;
1121 }
1122 }
1123
1124 cache_.emplace(result.in, result.out);
1125 return {result.in, result.out};
1126}
1127
1128template <class TIn, class TOut, class TDerived>
1131 PaymentSandbox& sb,
1132 ApplyView& afView,
1133 boost::container::flat_set<uint256>& ofrsToRm,
1134 TIn const& in)
1135{
1136 XRPL_ASSERT(cache_, "ripple::BookStep::fwdImp : cache is set");
1137
1138 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
1139
1140 auto remainingIn = in;
1141
1142 boost::container::flat_multiset<TIn> savedIns;
1143 savedIns.reserve(64);
1144 boost::container::flat_multiset<TOut> savedOuts;
1145 savedOuts.reserve(64);
1146
1147 // amt fed will be adjusted by owner funds (and may differ from the offer's
1148 // amounts - tho always <=)
1149 auto eachOffer = [&](auto& offer,
1150 TAmounts<TIn, TOut> const& ofrAmt,
1151 TAmounts<TIn, TOut> const& stpAmt,
1152 TOut const& ownerGives,
1153 std::uint32_t transferRateIn,
1154 std::uint32_t transferRateOut) mutable -> bool {
1155 XRPL_ASSERT(
1156 cache_, "ripple::BookStep::fwdImp::eachOffer : cache is set");
1157
1158 if (remainingIn <= beast::zero)
1159 return false;
1160
1161 bool processMore = true;
1162 auto ofrAdjAmt = ofrAmt;
1163 auto stpAdjAmt = stpAmt;
1164 auto ownerGivesAdj = ownerGives;
1165
1166 typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
1167 if (stpAmt.in <= remainingIn)
1168 {
1169 savedIns.insert(stpAmt.in);
1170 lastOut = savedOuts.insert(stpAmt.out);
1171 result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
1172 // consume the offer even if stepAmt.in == remainingIn
1173 processMore = true;
1174 }
1175 else
1176 {
1178 offer,
1179 ofrAdjAmt,
1180 stpAdjAmt,
1181 ownerGivesAdj,
1182 transferRateIn,
1183 transferRateOut,
1184 remainingIn);
1185 savedIns.insert(remainingIn);
1186 lastOut = savedOuts.insert(stpAdjAmt.out);
1187 result.out = sum(savedOuts);
1188 result.in = in;
1189
1190 processMore = false;
1191 }
1192
1193 if (result.out > cache_->out && result.in <= cache_->in)
1194 {
1195 // The step produced more output in the forward pass than the
1196 // reverse pass while consuming the same input (or less). If we
1197 // compute the input required to produce the cached output
1198 // (produced in the reverse step) and the input is equal to
1199 // the input consumed in the forward step, then consume the
1200 // input provided in the forward step and produce the output
1201 // requested from the reverse step.
1202 auto const lastOutAmt = *lastOut;
1203 savedOuts.erase(lastOut);
1204 auto const remainingOut = cache_->out - sum(savedOuts);
1205 auto ofrAdjAmtRev = ofrAmt;
1206 auto stpAdjAmtRev = stpAmt;
1207 auto ownerGivesAdjRev = ownerGives;
1209 offer,
1210 ofrAdjAmtRev,
1211 stpAdjAmtRev,
1212 ownerGivesAdjRev,
1213 transferRateIn,
1214 transferRateOut,
1215 remainingOut);
1216
1217 if (stpAdjAmtRev.in == remainingIn)
1218 {
1219 result.in = in;
1220 result.out = cache_->out;
1221
1222 savedIns.clear();
1223 savedIns.insert(result.in);
1224 savedOuts.clear();
1225 savedOuts.insert(result.out);
1226
1227 ofrAdjAmt = ofrAdjAmtRev;
1228 stpAdjAmt.in = remainingIn;
1229 stpAdjAmt.out = remainingOut;
1230 ownerGivesAdj = ownerGivesAdjRev;
1231 }
1232 else
1233 {
1234 // This is (likely) a problem case, and will be caught
1235 // with later checks
1236 savedOuts.insert(lastOutAmt);
1237 }
1238 }
1239
1240 remainingIn = in - result.in;
1241 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
1242
1243 // When the mantissas of two iou amounts differ by less than ten, then
1244 // subtracting them leaves a result of zero. This can cause the check
1245 // for (stpAmt.in > remainingIn) to incorrectly think an offer will be
1246 // funded after subtracting remainingIn.
1247 return processMore || offer.fully_consumed();
1248 };
1249
1250 {
1251 auto const prevStepDebtDir = [&] {
1252 if (prevStep_)
1253 return prevStep_->debtDirection(sb, StrandDirection::forward);
1254 return DebtDirection::issues;
1255 }();
1256 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
1257 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
1258 std::uint32_t const offersConsumed = std::get<1>(r);
1259 offersUsed_ = offersConsumed;
1260 SetUnion(ofrsToRm, toRm);
1261
1262 if (offersConsumed >= maxOffersToConsume_)
1263 {
1264 // Too many iterations, mark this strand as inactive (dry)
1265 if (!afView.rules().enabled(fix1515))
1266 {
1267 // Don't use the liquidity
1268 cache_.emplace(beast::zero, beast::zero);
1269 return {beast::zero, beast::zero};
1270 }
1271
1272 // Use the liquidity, but use this to mark the strand as inactive so
1273 // it's not used further
1274 inactive_ = true;
1275 }
1276 }
1277
1278 switch (remainingIn.signum())
1279 {
1280 case -1: {
1281 // something went very wrong
1282 JLOG(j_.error())
1283 << "BookStep remainingIn < 0 " << to_string(remainingIn);
1284 UNREACHABLE("ripple::BookStep::fwdImp : remaining less than zero");
1285 cache_.emplace(beast::zero, beast::zero);
1286 return {beast::zero, beast::zero};
1287 }
1288 case 0: {
1289 // due to normalization, remainingIn can be zero without
1290 // result.in == in. Force result.in == in for this case
1291 result.in = in;
1292 }
1293 }
1294
1295 cache_.emplace(result.in, result.out);
1296 return {result.in, result.out};
1297}
1298
1299template <class TIn, class TOut, class TDerived>
1302 PaymentSandbox& sb,
1303 ApplyView& afView,
1304 EitherAmount const& in)
1305{
1306 if (!cache_)
1307 {
1308 JLOG(j_.trace()) << "Expected valid cache in validFwd";
1309 return {false, EitherAmount(TOut(beast::zero))};
1310 }
1311
1312 auto const savCache = *cache_;
1313
1314 try
1315 {
1316 boost::container::flat_set<uint256> dummy;
1317 fwdImp(sb, afView, dummy, get<TIn>(in)); // changes cache
1318 }
1319 catch (FlowException const&)
1320 {
1321 return {false, EitherAmount(TOut(beast::zero))};
1322 }
1323
1324 if (!(checkNear(savCache.in, cache_->in) &&
1325 checkNear(savCache.out, cache_->out)))
1326 {
1327 JLOG(j_.warn()) << "Strand re-execute check failed."
1328 << " ExpectedIn: " << to_string(savCache.in)
1329 << " CachedIn: " << to_string(cache_->in)
1330 << " ExpectedOut: " << to_string(savCache.out)
1331 << " CachedOut: " << to_string(cache_->out);
1332 return {false, EitherAmount(cache_->out)};
1333 }
1334 return {true, EitherAmount(cache_->out)};
1335}
1336
1337template <class TIn, class TOut, class TDerived>
1338TER
1340{
1341 if (book_.in == book_.out)
1342 {
1343 JLOG(j_.debug()) << "BookStep: Book with same in and out issuer "
1344 << *this;
1345 return temBAD_PATH;
1346 }
1347 if (!isConsistent(book_.in) || !isConsistent(book_.out))
1348 {
1349 JLOG(j_.debug()) << "Book: currency is inconsistent with issuer."
1350 << *this;
1351 return temBAD_PATH;
1352 }
1353
1354 // Do not allow two books to output the same issue. This may cause offers on
1355 // one step to unfund offers in another step.
1356 if (!ctx.seenBookOuts.insert(book_.out).second ||
1357 ctx.seenDirectIssues[0].count(book_.out))
1358 {
1359 JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
1360 return temBAD_PATH_LOOP;
1361 }
1362
1363 if (ctx.seenDirectIssues[1].count(book_.out))
1364 {
1365 JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
1366 return temBAD_PATH_LOOP;
1367 }
1368
1369 auto issuerExists = [](ReadView const& view, Issue const& iss) -> bool {
1370 return isXRP(iss.account) || view.read(keylet::account(iss.account));
1371 };
1372
1373 if (!issuerExists(ctx.view, book_.in) || !issuerExists(ctx.view, book_.out))
1374 {
1375 JLOG(j_.debug()) << "BookStep: deleted issuer detected: " << *this;
1376 return tecNO_ISSUER;
1377 }
1378
1379 if (ctx.prevStep)
1380 {
1381 if (auto const prev = ctx.prevStep->directStepSrcAcct())
1382 {
1383 auto const& view = ctx.view;
1384 auto const& cur = book_.in.account;
1385
1386 auto sle = view.read(keylet::line(*prev, cur, book_.in.currency));
1387 if (!sle)
1388 return terNO_LINE;
1389 if ((*sle)[sfFlags] &
1390 ((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple))
1391 return terNO_RIPPLE;
1392 }
1393 }
1394
1395 return tesSUCCESS;
1396}
1397
1398//------------------------------------------------------------------------------
1399
1400namespace test {
1401// Needed for testing
1402
1403template <class TIn, class TOut, class TDerived>
1404static bool
1405equalHelper(Step const& step, ripple::Book const& book)
1406{
1407 if (auto bs = dynamic_cast<BookStep<TIn, TOut, TDerived> const*>(&step))
1408 return book == bs->book();
1409 return false;
1410}
1411
1412bool
1413bookStepEqual(Step const& step, ripple::Book const& book)
1414{
1415 bool const inXRP = isXRP(book.in.currency);
1416 bool const outXRP = isXRP(book.out.currency);
1417 if (inXRP && outXRP)
1418 {
1419 UNREACHABLE("ripple::test::bookStepEqual : no XRP to XRP book step");
1420 return false; // no such thing as xrp/xrp book step
1421 }
1422 if (inXRP && !outXRP)
1423 return equalHelper<
1424 XRPAmount,
1425 IOUAmount,
1427 if (!inXRP && outXRP)
1428 return equalHelper<
1429 IOUAmount,
1430 XRPAmount,
1432 if (!inXRP && !outXRP)
1433 return equalHelper<
1434 IOUAmount,
1435 IOUAmount,
1437 return false;
1438}
1439} // namespace test
1440
1441//------------------------------------------------------------------------------
1442
1443template <class TIn, class TOut>
1446{
1447 TER ter = tefINTERNAL;
1449 if (ctx.offerCrossing)
1450 {
1451 auto offerCrossingStep =
1452 std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx, in, out);
1453 ter = offerCrossingStep->check(ctx);
1454 r = std::move(offerCrossingStep);
1455 }
1456 else // payment
1457 {
1458 auto paymentStep =
1459 std::make_unique<BookPaymentStep<TIn, TOut>>(ctx, in, out);
1460 ter = paymentStep->check(ctx);
1461 r = std::move(paymentStep);
1462 }
1463 if (ter != tesSUCCESS)
1464 return {ter, nullptr};
1465
1466 return {tesSUCCESS, std::move(r)};
1467}
1468
1470make_BookStepII(StrandContext const& ctx, Issue const& in, Issue const& out)
1471{
1472 return make_BookStepHelper<IOUAmount, IOUAmount>(ctx, in, out);
1473}
1474
1477{
1478 return make_BookStepHelper<IOUAmount, XRPAmount>(ctx, in, xrpIssue());
1479}
1480
1483{
1484 return make_BookStepHelper<XRPAmount, IOUAmount>(ctx, xrpIssue(), out);
1485}
1486
1487} // namespace ripple
T accumulate(T... args)
A generic endpoint for log messages.
Definition: Journal.h:59
Stream error() const
Definition: Journal.h:335
Stream debug() const
Definition: Journal.h:317
Stream trace() const
Severity stream access functions.
Definition: Journal.h:311
Stream warn() const
Definition: Journal.h:329
AccountID account() const
Definition: AMMContext.h:102
Represents synthetic AMM offer in BookStep.
Definition: AMMOffer.h:40
Quality quality() const noexcept
Definition: AMMOffer.h:69
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:140
std::uint32_t getOfrInRate(Step const *prevStep, AccountID const &owner, std::uint32_t trIn) const
Definition: BookStep.cpp:489
static Quality getQuality(std::optional< Quality > const &limitQuality)
Definition: BookStep.cpp:380
BookOfferCrossingStep(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:392
bool checkQualityThreshold(Quality const &quality) const
Definition: BookStep.cpp:462
std::string logString() const override
Definition: BookStep.cpp:559
std::uint32_t getOfrOutRate(Step const *prevStep, AccountID const &owner, AccountID const &strandDst, std::uint32_t trOut) const
Definition: BookStep.cpp:504
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:404
Quality const qualityThreshold_
Definition: BookStep.cpp:566
std::optional< Quality > qualityThreshold(Quality const &lobQuality) const
Definition: BookStep.cpp:478
Quality adjustQualityWithFees(ReadView const &v, Quality const &ofrQ, DebtDirection prevStepDir, WaiveTransferFee waiveFee, OfferType offerType, Rules const &rules) const
Definition: BookStep.cpp:518
bool limitSelfCrossQuality(AccountID const &, AccountID const &, Offer< TIn, TOut > const &offer, std::optional< Quality > &, FlowOfferStream< TIn, TOut > &, bool) const
Definition: BookStep.cpp:282
Quality adjustQualityWithFees(ReadView const &v, Quality const &ofrQ, DebtDirection prevStepDir, WaiveTransferFee waiveFee, OfferType, Rules const &) const
Definition: BookStep.cpp:327
std::uint32_t getOfrOutRate(Step const *, AccountID const &, AccountID const &, std::uint32_t trOut) const
Definition: BookStep.cpp:317
std::uint32_t getOfrInRate(Step const *, AccountID const &, std::uint32_t trIn) const
Definition: BookStep.cpp:310
bool checkQualityThreshold(Quality const &quality) const
Definition: BookStep.cpp:295
std::optional< Quality > qualityThreshold(Quality const &lobQuality) const
Definition: BookStep.cpp:303
std::string logString() const override
Definition: BookStep.cpp:360
std::optional< AMMOffer< TIn, TOut > > getAMMOffer(ReadView const &view, std::optional< Quality > const &clobQuality) const
Definition: BookStep.cpp:922
std::optional< AMMLiquidity< TIn, TOut > > ammLiquidity_
Definition: BookStep.cpp:69
friend bool operator==(BookStep const &lhs, BookStep const &rhs)
Definition: BookStep.cpp:201
std::uint32_t offersUsed() const override
Definition: BookStep.cpp:650
std::optional< Book > bookStepBook() const override
Definition: BookStep.cpp:144
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
std::optional< EitherAmount > cachedOut() const override
Definition: BookStep.cpp:129
std::optional< std::pair< Quality, OfferType > > tipOfferQuality(ReadView const &view) const
Definition: BookStep.cpp:973
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:873
std::pair< std::optional< QualityFunction >, DebtDirection > getQualityFunc(ReadView const &v, DebtDirection prevStepDir) const override
Definition: BookStep.cpp:608
TER check(StrandContext const &ctx) const
Definition: BookStep.cpp:1339
std::pair< boost::container::flat_set< uint256 >, std::uint32_t > forEachOffer(PaymentSandbox &sb, ApplyView &afView, DebtDirection prevStepDebtDir, Callback &callback) const
Definition: BookStep.cpp:715
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
Definition: BookStep.cpp:1301
Book const & book() const
Definition: BookStep.cpp:115
std::optional< EitherAmount > cachedIn() const override
Definition: BookStep.cpp:121
uint32_t const maxOffersToConsume_
Definition: BookStep.cpp:49
bool equal(Step const &rhs) const override
Definition: BookStep.cpp:573
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
Definition: BookStep.cpp:137
std::pair< TIn, TOut > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, TOut const &out)
Definition: BookStep.cpp:1009
Step const *const prevStep_
Definition: BookStep.cpp:54
friend bool operator!=(BookStep const &lhs, BookStep const &rhs)
Definition: BookStep.cpp:207
std::string logStringImpl(char const *name) const
Definition: BookStep.cpp:189
BookStep(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:93
std::pair< TIn, TOut > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, TIn const &in)
Definition: BookStep.cpp:1130
std::optional< QualityFunction > tipOfferQualityF(ReadView const &view) const
Definition: BookStep.cpp:987
AccountID strandDst_
Definition: BookStep.cpp:52
bool inactive() const override
Definition: BookStep.cpp:182
static uint32_t getMaxOffersToConsume(StrandContext const &ctx)
Definition: BookStep.cpp:85
std::optional< std::variant< Quality, AMMOffer< TIn, TOut > > > tip(ReadView const &view) const
Definition: BookStep.cpp:933
std::pair< std::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection prevStepDir) const override
Definition: BookStep.cpp:582
bool const ownerPaysTransferFee_
Definition: BookStep.cpp:55
beast::Journal const j_
Definition: BookStep.cpp:70
std::optional< Cache > cache_
Definition: BookStep.cpp:82
AccountID strandSrc_
Definition: BookStep.cpp:51
Iterates and consumes raw offers in an order book.
Definition: BookTip.h:38
bool step(beast::Journal j)
Erases the current offer and advance to the next offer.
Definition: BookTip.cpp:34
Quality const & quality() const noexcept
Definition: BookTip.h:66
Specifies an order book.
Definition: Book.h:34
Issue in
Definition: Book.h:36
Issue out
Definition: Book.h:37
Presents and consumes the offers in an order book.
Definition: OfferStream.h:176
Floating point representation of amounts with high dynamic range.
Definition: IOUAmount.h:45
A currency issued by an account.
Definition: Issue.h:36
AccountID account
Definition: Issue.h:39
Currency currency
Definition: Issue.h:38
A wrapper which makes credits unavailable to balances.
Average quality of a path as a function of out: q(out) = m * out + b, where m = -1 / poolGets,...
A view into a ledger.
Definition: ReadView.h:55
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition: ReadView.h:115
virtual Rules const & rules() const =0
Returns the tx processing rules.
Rules controlling protocol behavior.
Definition: Rules.h:35
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:122
static std::uint64_t const uRateOne
Definition: STAmount.h:80
Discardable, editable view to a ledger.
Definition: Sandbox.h:35
A step in a payment path.
Definition: Steps.h:83
virtual std::optional< Book > bookStepBook() const
If this step is a BookStep, return the book.
Definition: Steps.h:221
virtual std::optional< AccountID > directStepSrcAcct() const
If this step is DirectStepI (IOU->IOU direct step), return the src account.
Definition: Steps.h:142
Rules const & rules() const override
Returns the tx processing rules.
T make_pair(T... args)
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:422
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:220
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:160
static bool equalHelper(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1405
bool bookStepEqual(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1413
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:118
static auto sum(TCollection const &col)
Definition: BookStep.cpp:999
bool isConsistent(Book const &book)
Definition: Book.cpp:25
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
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:658
StrandDirection
Definition: Steps.h:41
std::uint16_t getTradingFee(ReadView const &view, SLE const &ammSle, AccountID const &account)
Get AMM trading fee for the given account.
Definition: AMMUtils.cpp:177
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition: View.cpp:650
@ lsfHighNoRipple
@ lsfRequireAuth
@ lsfLowNoRipple
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
static bool isDefaultPath(STPath const &path)
Definition: Pathfinder.cpp:457
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition: STAmount.cpp:451
@ tefINTERNAL
Definition: TER.h:173
Quality composed_quality(Quality const &lhs, Quality const &rhs)
Definition: Quality.cpp:153
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::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
Definition: BookStep.cpp:1482
DebtDirection
Definition: Steps.h:39
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
Definition: BookStep.cpp:1476
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition: PaySteps.cpp:37
WaiveTransferFee
Definition: View.h:46
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)
Definition: BookStep.cpp:689
@ tecNO_ISSUER
Definition: TER.h:286
@ tecINVARIANT_FAILED
Definition: TER.h:300
static std::pair< TER, std::unique_ptr< Step > > make_BookStepHelper(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1445
@ tesSUCCESS
Definition: TER.h:242
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1470
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Definition: IOUAmount.cpp:182
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
@ tapNONE
Definition: ApplyView.h:31
@ terNO_RIPPLE
Definition: TER.h:224
@ terNO_LINE
Definition: TER.h:219
Rate const parityRate
A transfer rate signifying a 1:1 exchange.
@ temBAD_PATH_LOOP
Definition: TER.h:97
@ temBAD_PATH
Definition: TER.h:96
T str(T... args)
Cache(TIn const &in_, TOut const &out_)
Definition: BookStep.cpp:77
uint256 key
Definition: Keylet.h:40
std::uint32_t value
Definition: Rate.h:39
Context needed to build Strand Steps and for error checking.
Definition: Steps.h:526
beast::Journal const j
Definition: Steps.h:554
boost::container::flat_set< Issue > & seenBookOuts
A strand may not include an offer that output the same issue more than once.
Definition: Steps.h:552
ReadView const & view
Current ReadView.
Definition: Steps.h:527
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:548
Step const *const prevStep
The previous step in the strand.
Definition: Steps.h:542
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
Definition: Steps.h:536
AMMContext & ammContext
Definition: Steps.h:553