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