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