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, ctx.domainID)
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 // amm doesn't support domain yet
841 if (book_.domain)
842 return true;
843
844 // If offer crossing then use either LOB quality or nullopt
845 // to prevent AMM being blocked by a lower quality LOB.
846 auto const qualityThreshold = [&]() -> std::optional<Quality> {
847 if (sb.rules().enabled(fixAMMv1_1) && lobQuality)
848 return static_cast<TDerived const*>(this)->qualityThreshold(
849 *lobQuality);
850 return lobQuality;
851 }();
852 auto ammOffer = getAMMOffer(sb, qualityThreshold);
853 return !ammOffer || execOffer(*ammOffer);
854 };
855
856 if (offers.step())
857 {
858 if (tryAMM(offers.tip().quality()))
859 {
860 do
861 {
862 if (!execOffer(offers.tip()))
863 break;
864 } while (offers.step());
865 }
866 }
867 else
868 {
869 // Might have AMM offer if there are no LOB offers.
870 tryAMM(std::nullopt);
871 }
872
873 return {offers.permToRemove(), counter.count()};
874}
875
876template <class TIn, class TOut, class TDerived>
877template <template <typename, typename> typename Offer>
878void
880 PaymentSandbox& sb,
881 Offer<TIn, TOut>& offer,
882 TAmounts<TIn, TOut> const& ofrAmt,
883 TAmounts<TIn, TOut> const& stepAmt,
884 TOut const& ownerGives) const
885{
886 if (!offer.checkInvariant(ofrAmt, j_))
887 {
888 // purposely written as separate if statements so we get logging even
889 // when the amendment isn't active.
890 if (sb.rules().enabled(fixAMMOverflowOffer))
891 {
892 Throw<FlowException>(
893 tecINVARIANT_FAILED, "AMM pool product invariant failed.");
894 }
895 }
896
897 // The offer owner gets the ofrAmt. The difference between ofrAmt and
898 // stepAmt is a transfer fee that goes to book_.in.account
899 {
900 auto const dr = offer.send(
901 sb,
902 book_.in.account,
903 offer.owner(),
904 toSTAmount(ofrAmt.in, book_.in),
905 j_);
906 if (dr != tesSUCCESS)
907 Throw<FlowException>(dr);
908 }
909
910 // The offer owner pays `ownerGives`. The difference between ownerGives and
911 // stepAmt is a transfer fee that goes to book_.out.account
912 {
913 auto const cr = offer.send(
914 sb,
915 offer.owner(),
916 book_.out.account,
917 toSTAmount(ownerGives, book_.out),
918 j_);
919 if (cr != tesSUCCESS)
920 Throw<FlowException>(cr);
921 }
922
923 offer.consume(sb, ofrAmt);
924}
925
926template <class TIn, class TOut, class TDerived>
929 ReadView const& view,
930 std::optional<Quality> const& clobQuality) const
931{
932 if (ammLiquidity_)
933 return ammLiquidity_->getOffer(view, clobQuality);
934 return std::nullopt;
935}
936
937template <class TIn, class TOut, class TDerived>
940{
941 // This can be simplified (and sped up) if directories are never empty.
942 Sandbox sb(&view, tapNONE);
943 BookTip bt(sb, book_);
944 auto const lobQuality =
945 bt.step(j_) ? std::optional<Quality>(bt.quality()) : std::nullopt;
946 // Multi-path offer generates an offer with the quality
947 // calculated from the offer size and the quality is constant in this case.
948 // Single path offer quality changes with the offer size. Spot price quality
949 // (SPQ) can't be used in this case as the upper bound quality because
950 // even if SPQ quality is better than LOB quality, it might not be possible
951 // to generate AMM offer at or better quality than LOB quality. Another
952 // factor to consider is limit quality on offer crossing. If LOB quality
953 // is greater than limit quality then use LOB quality when generating AMM
954 // offer, otherwise don't use quality threshold when generating AMM offer.
955 // AMM or LOB offer, whether multi-path or single path then can be selected
956 // based on the best offer quality. Using the quality to generate AMM offer
957 // in this case also prevents the payment engine from going into multiple
958 // iterations to cross a LOB offer. This happens when AMM changes
959 // the out amount at the start of iteration to match the limitQuality
960 // on offer crossing but AMM can't generate the offer at this quality,
961 // as the result a LOB offer is partially crossed, and it might take a few
962 // iterations to fully cross the offer.
963 auto const qualityThreshold = [&]() -> std::optional<Quality> {
964 if (view.rules().enabled(fixAMMv1_1) && lobQuality)
965 return static_cast<TDerived const*>(this)->qualityThreshold(
966 *lobQuality);
967 return std::nullopt;
968 }();
969 // AMM quality is better or no LOB offer
970 if (auto const ammOffer = getAMMOffer(view, qualityThreshold); ammOffer &&
971 ((lobQuality && ammOffer->quality() > lobQuality) || !lobQuality))
972 return ammOffer;
973 // LOB quality is better or nullopt
974 return lobQuality;
975}
976
977template <class TIn, class TOut, class TDerived>
978auto
981{
982 if (auto const res = tip(view); !res)
983 return std::nullopt;
984 else if (auto const q = std::get_if<Quality>(&(*res)))
985 return std::make_pair(*q, OfferType::CLOB);
986 else
987 return std::make_pair(
988 std::get<AMMOffer<TIn, TOut>>(*res).quality(), OfferType::AMM);
989}
990
991template <class TIn, class TOut, class TDerived>
994{
995 if (auto const res = tip(view); !res)
996 return std::nullopt;
997 else if (auto const q = std::get_if<Quality>(&(*res)))
999 else
1000 return std::get<AMMOffer<TIn, TOut>>(*res).getQualityFunc();
1001}
1002
1003template <class TCollection>
1004static auto
1005sum(TCollection const& col)
1006{
1007 using TResult = std::decay_t<decltype(*col.begin())>;
1008 if (col.empty())
1009 return TResult{beast::zero};
1010 return std::accumulate(col.begin() + 1, col.end(), *col.begin());
1011};
1012
1013template <class TIn, class TOut, class TDerived>
1016 PaymentSandbox& sb,
1017 ApplyView& afView,
1018 boost::container::flat_set<uint256>& ofrsToRm,
1019 TOut const& out)
1020{
1021 cache_.reset();
1022
1023 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
1024
1025 auto remainingOut = out;
1026
1027 boost::container::flat_multiset<TIn> savedIns;
1028 savedIns.reserve(64);
1029 boost::container::flat_multiset<TOut> savedOuts;
1030 savedOuts.reserve(64);
1031
1032 /* amt fed will be adjusted by owner funds (and may differ from the offer's
1033 amounts - tho always <=)
1034 Return true to continue to receive offers, false to stop receiving offers.
1035 */
1036 auto eachOffer = [&](auto& offer,
1037 TAmounts<TIn, TOut> const& ofrAmt,
1038 TAmounts<TIn, TOut> const& stpAmt,
1039 TOut const& ownerGives,
1040 std::uint32_t transferRateIn,
1041 std::uint32_t transferRateOut) mutable -> bool {
1042 if (remainingOut <= beast::zero)
1043 return false;
1044
1045 if (stpAmt.out <= remainingOut)
1046 {
1047 savedIns.insert(stpAmt.in);
1048 savedOuts.insert(stpAmt.out);
1049 result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
1050 remainingOut = out - result.out;
1051 this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
1052 // return true b/c even if the payment is satisfied,
1053 // we need to consume the offer
1054 return true;
1055 }
1056 else
1057 {
1058 auto ofrAdjAmt = ofrAmt;
1059 auto stpAdjAmt = stpAmt;
1060 auto ownerGivesAdj = ownerGives;
1062 offer,
1063 ofrAdjAmt,
1064 stpAdjAmt,
1065 ownerGivesAdj,
1066 transferRateIn,
1067 transferRateOut,
1068 remainingOut);
1069 remainingOut = beast::zero;
1070 savedIns.insert(stpAdjAmt.in);
1071 savedOuts.insert(remainingOut);
1072 result.in = sum(savedIns);
1073 result.out = out;
1074 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
1075
1076 // Explicitly check whether the offer is funded. Given that we have
1077 // (stpAmt.out > remainingOut), it's natural to assume the offer
1078 // will still be funded after consuming remainingOut but that is
1079 // not always the case. If the mantissas of two IOU amounts differ
1080 // by less than ten, then subtracting them leaves a zero.
1081 return offer.fully_consumed();
1082 }
1083 };
1084
1085 {
1086 auto const prevStepDebtDir = [&] {
1087 if (prevStep_)
1088 return prevStep_->debtDirection(sb, StrandDirection::reverse);
1089 return DebtDirection::issues;
1090 }();
1091 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
1092 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
1093 std::uint32_t const offersConsumed = std::get<1>(r);
1094 offersUsed_ = offersConsumed;
1095 SetUnion(ofrsToRm, toRm);
1096
1097 if (offersConsumed >= maxOffersToConsume_)
1098 {
1099 // Too many iterations, mark this strand as inactive
1100 if (!afView.rules().enabled(fix1515))
1101 {
1102 // Don't use the liquidity
1103 cache_.emplace(beast::zero, beast::zero);
1104 return {beast::zero, beast::zero};
1105 }
1106
1107 // Use the liquidity, but use this to mark the strand as inactive so
1108 // it's not used further
1109 inactive_ = true;
1110 }
1111 }
1112
1113 switch (remainingOut.signum())
1114 {
1115 case -1: {
1116 // something went very wrong
1117 JLOG(j_.error())
1118 << "BookStep remainingOut < 0 " << to_string(remainingOut);
1119 UNREACHABLE("ripple::BookStep::revImp : remaining less than zero");
1120 cache_.emplace(beast::zero, beast::zero);
1121 return {beast::zero, beast::zero};
1122 }
1123 case 0: {
1124 // due to normalization, remainingOut can be zero without
1125 // result.out == out. Force result.out == out for this case
1126 result.out = out;
1127 }
1128 }
1129
1130 cache_.emplace(result.in, result.out);
1131 return {result.in, result.out};
1132}
1133
1134template <class TIn, class TOut, class TDerived>
1137 PaymentSandbox& sb,
1138 ApplyView& afView,
1139 boost::container::flat_set<uint256>& ofrsToRm,
1140 TIn const& in)
1141{
1142 XRPL_ASSERT(cache_, "ripple::BookStep::fwdImp : cache is set");
1143
1144 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
1145
1146 auto remainingIn = in;
1147
1148 boost::container::flat_multiset<TIn> savedIns;
1149 savedIns.reserve(64);
1150 boost::container::flat_multiset<TOut> savedOuts;
1151 savedOuts.reserve(64);
1152
1153 // amt fed will be adjusted by owner funds (and may differ from the offer's
1154 // amounts - tho always <=)
1155 auto eachOffer = [&](auto& offer,
1156 TAmounts<TIn, TOut> const& ofrAmt,
1157 TAmounts<TIn, TOut> const& stpAmt,
1158 TOut const& ownerGives,
1159 std::uint32_t transferRateIn,
1160 std::uint32_t transferRateOut) mutable -> bool {
1161 XRPL_ASSERT(
1162 cache_, "ripple::BookStep::fwdImp::eachOffer : cache is set");
1163
1164 if (remainingIn <= beast::zero)
1165 return false;
1166
1167 bool processMore = true;
1168 auto ofrAdjAmt = ofrAmt;
1169 auto stpAdjAmt = stpAmt;
1170 auto ownerGivesAdj = ownerGives;
1171
1172 typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
1173 if (stpAmt.in <= remainingIn)
1174 {
1175 savedIns.insert(stpAmt.in);
1176 lastOut = savedOuts.insert(stpAmt.out);
1177 result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
1178 // consume the offer even if stepAmt.in == remainingIn
1179 processMore = true;
1180 }
1181 else
1182 {
1184 offer,
1185 ofrAdjAmt,
1186 stpAdjAmt,
1187 ownerGivesAdj,
1188 transferRateIn,
1189 transferRateOut,
1190 remainingIn);
1191 savedIns.insert(remainingIn);
1192 lastOut = savedOuts.insert(stpAdjAmt.out);
1193 result.out = sum(savedOuts);
1194 result.in = in;
1195
1196 processMore = false;
1197 }
1198
1199 if (result.out > cache_->out && result.in <= cache_->in)
1200 {
1201 // The step produced more output in the forward pass than the
1202 // reverse pass while consuming the same input (or less). If we
1203 // compute the input required to produce the cached output
1204 // (produced in the reverse step) and the input is equal to
1205 // the input consumed in the forward step, then consume the
1206 // input provided in the forward step and produce the output
1207 // requested from the reverse step.
1208 auto const lastOutAmt = *lastOut;
1209 savedOuts.erase(lastOut);
1210 auto const remainingOut = cache_->out - sum(savedOuts);
1211 auto ofrAdjAmtRev = ofrAmt;
1212 auto stpAdjAmtRev = stpAmt;
1213 auto ownerGivesAdjRev = ownerGives;
1215 offer,
1216 ofrAdjAmtRev,
1217 stpAdjAmtRev,
1218 ownerGivesAdjRev,
1219 transferRateIn,
1220 transferRateOut,
1221 remainingOut);
1222
1223 if (stpAdjAmtRev.in == remainingIn)
1224 {
1225 result.in = in;
1226 result.out = cache_->out;
1227
1228 savedIns.clear();
1229 savedIns.insert(result.in);
1230 savedOuts.clear();
1231 savedOuts.insert(result.out);
1232
1233 ofrAdjAmt = ofrAdjAmtRev;
1234 stpAdjAmt.in = remainingIn;
1235 stpAdjAmt.out = remainingOut;
1236 ownerGivesAdj = ownerGivesAdjRev;
1237 }
1238 else
1239 {
1240 // This is (likely) a problem case, and will be caught
1241 // with later checks
1242 savedOuts.insert(lastOutAmt);
1243 }
1244 }
1245
1246 remainingIn = in - result.in;
1247 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
1248
1249 // When the mantissas of two iou amounts differ by less than ten, then
1250 // subtracting them leaves a result of zero. This can cause the check
1251 // for (stpAmt.in > remainingIn) to incorrectly think an offer will be
1252 // funded after subtracting remainingIn.
1253 return processMore || offer.fully_consumed();
1254 };
1255
1256 {
1257 auto const prevStepDebtDir = [&] {
1258 if (prevStep_)
1259 return prevStep_->debtDirection(sb, StrandDirection::forward);
1260 return DebtDirection::issues;
1261 }();
1262 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
1263 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
1264 std::uint32_t const offersConsumed = std::get<1>(r);
1265 offersUsed_ = offersConsumed;
1266 SetUnion(ofrsToRm, toRm);
1267
1268 if (offersConsumed >= maxOffersToConsume_)
1269 {
1270 // Too many iterations, mark this strand as inactive (dry)
1271 if (!afView.rules().enabled(fix1515))
1272 {
1273 // Don't use the liquidity
1274 cache_.emplace(beast::zero, beast::zero);
1275 return {beast::zero, beast::zero};
1276 }
1277
1278 // Use the liquidity, but use this to mark the strand as inactive so
1279 // it's not used further
1280 inactive_ = true;
1281 }
1282 }
1283
1284 switch (remainingIn.signum())
1285 {
1286 case -1: {
1287 // something went very wrong
1288 JLOG(j_.error())
1289 << "BookStep remainingIn < 0 " << to_string(remainingIn);
1290 UNREACHABLE("ripple::BookStep::fwdImp : remaining less than zero");
1291 cache_.emplace(beast::zero, beast::zero);
1292 return {beast::zero, beast::zero};
1293 }
1294 case 0: {
1295 // due to normalization, remainingIn can be zero without
1296 // result.in == in. Force result.in == in for this case
1297 result.in = in;
1298 }
1299 }
1300
1301 cache_.emplace(result.in, result.out);
1302 return {result.in, result.out};
1303}
1304
1305template <class TIn, class TOut, class TDerived>
1308 PaymentSandbox& sb,
1309 ApplyView& afView,
1310 EitherAmount const& in)
1311{
1312 if (!cache_)
1313 {
1314 JLOG(j_.trace()) << "Expected valid cache in validFwd";
1315 return {false, EitherAmount(TOut(beast::zero))};
1316 }
1317
1318 auto const savCache = *cache_;
1319
1320 try
1321 {
1322 boost::container::flat_set<uint256> dummy;
1323 fwdImp(sb, afView, dummy, get<TIn>(in)); // changes cache
1324 }
1325 catch (FlowException const&)
1326 {
1327 return {false, EitherAmount(TOut(beast::zero))};
1328 }
1329
1330 if (!(checkNear(savCache.in, cache_->in) &&
1331 checkNear(savCache.out, cache_->out)))
1332 {
1333 JLOG(j_.warn()) << "Strand re-execute check failed."
1334 << " ExpectedIn: " << to_string(savCache.in)
1335 << " CachedIn: " << to_string(cache_->in)
1336 << " ExpectedOut: " << to_string(savCache.out)
1337 << " CachedOut: " << to_string(cache_->out);
1338 return {false, EitherAmount(cache_->out)};
1339 }
1340 return {true, EitherAmount(cache_->out)};
1341}
1342
1343template <class TIn, class TOut, class TDerived>
1344TER
1346{
1347 if (book_.in == book_.out)
1348 {
1349 JLOG(j_.debug()) << "BookStep: Book with same in and out issuer "
1350 << *this;
1351 return temBAD_PATH;
1352 }
1353 if (!isConsistent(book_.in) || !isConsistent(book_.out))
1354 {
1355 JLOG(j_.debug()) << "Book: currency is inconsistent with issuer."
1356 << *this;
1357 return temBAD_PATH;
1358 }
1359
1360 // Do not allow two books to output the same issue. This may cause offers on
1361 // one step to unfund offers in another step.
1362 if (!ctx.seenBookOuts.insert(book_.out).second ||
1363 ctx.seenDirectIssues[0].count(book_.out))
1364 {
1365 JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
1366 return temBAD_PATH_LOOP;
1367 }
1368
1369 if (ctx.seenDirectIssues[1].count(book_.out))
1370 {
1371 JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
1372 return temBAD_PATH_LOOP;
1373 }
1374
1375 auto issuerExists = [](ReadView const& view, Issue const& iss) -> bool {
1376 return isXRP(iss.account) || view.read(keylet::account(iss.account));
1377 };
1378
1379 if (!issuerExists(ctx.view, book_.in) || !issuerExists(ctx.view, book_.out))
1380 {
1381 JLOG(j_.debug()) << "BookStep: deleted issuer detected: " << *this;
1382 return tecNO_ISSUER;
1383 }
1384
1385 if (ctx.prevStep)
1386 {
1387 if (auto const prev = ctx.prevStep->directStepSrcAcct())
1388 {
1389 auto const& view = ctx.view;
1390 auto const& cur = book_.in.account;
1391
1392 auto sle = view.read(keylet::line(*prev, cur, book_.in.currency));
1393 if (!sle)
1394 return terNO_LINE;
1395 if ((*sle)[sfFlags] &
1396 ((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple))
1397 return terNO_RIPPLE;
1398 }
1399 }
1400
1401 return tesSUCCESS;
1402}
1403
1404//------------------------------------------------------------------------------
1405
1406namespace test {
1407// Needed for testing
1408
1409template <class TIn, class TOut, class TDerived>
1410static bool
1411equalHelper(Step const& step, ripple::Book const& book)
1412{
1413 if (auto bs = dynamic_cast<BookStep<TIn, TOut, TDerived> const*>(&step))
1414 return book == bs->book();
1415 return false;
1416}
1417
1418bool
1419bookStepEqual(Step const& step, ripple::Book const& book)
1420{
1421 bool const inXRP = isXRP(book.in.currency);
1422 bool const outXRP = isXRP(book.out.currency);
1423 if (inXRP && outXRP)
1424 {
1425 UNREACHABLE("ripple::test::bookStepEqual : no XRP to XRP book step");
1426 return false; // no such thing as xrp/xrp book step
1427 }
1428 if (inXRP && !outXRP)
1429 return equalHelper<
1430 XRPAmount,
1431 IOUAmount,
1433 if (!inXRP && outXRP)
1434 return equalHelper<
1435 IOUAmount,
1436 XRPAmount,
1438 if (!inXRP && !outXRP)
1439 return equalHelper<
1440 IOUAmount,
1441 IOUAmount,
1443 return false;
1444}
1445} // namespace test
1446
1447//------------------------------------------------------------------------------
1448
1449template <class TIn, class TOut>
1452{
1453 TER ter = tefINTERNAL;
1455 if (ctx.offerCrossing)
1456 {
1457 auto offerCrossingStep =
1458 std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx, in, out);
1459 ter = offerCrossingStep->check(ctx);
1460 r = std::move(offerCrossingStep);
1461 }
1462 else // payment
1463 {
1464 auto paymentStep =
1465 std::make_unique<BookPaymentStep<TIn, TOut>>(ctx, in, out);
1466 ter = paymentStep->check(ctx);
1467 r = std::move(paymentStep);
1468 }
1469 if (ter != tesSUCCESS)
1470 return {ter, nullptr};
1471
1472 return {tesSUCCESS, std::move(r)};
1473}
1474
1476make_BookStepII(StrandContext const& ctx, Issue const& in, Issue const& out)
1477{
1478 return make_BookStepHelper<IOUAmount, IOUAmount>(ctx, in, out);
1479}
1480
1483{
1484 return make_BookStepHelper<IOUAmount, XRPAmount>(ctx, in, xrpIssue());
1485}
1486
1489{
1490 return make_BookStepHelper<XRPAmount, IOUAmount>(ctx, xrpIssue(), out);
1491}
1492
1493} // 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:144
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:928
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:979
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:879
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:1345
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:1307
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:1015
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:1136
std::optional< QualityFunction > tipOfferQualityF(ReadView const &view) const
Definition: BookStep.cpp:993
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:939
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:36
Issue in
Definition: Book.h:38
Issue out
Definition: Book.h:39
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:46
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:38
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:86
virtual std::optional< Book > bookStepBook() const
If this step is a BookStep, return the book.
Definition: Steps.h:224
virtual std::optional< AccountID > directStepSrcAcct() const
If this step is DirectStepI (IOU->IOU direct step), return the src account.
Definition: Steps.h:145
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:446
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:244
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:184
static bool equalHelper(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1411
bool bookStepEqual(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1419
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:1005
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:44
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:761
@ lsfHighNoRipple
@ lsfRequireAuth
@ lsfLowNoRipple
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
static bool isDefaultPath(STPath const &path)
Definition: Pathfinder.cpp:463
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition: STAmount.cpp:486
@ 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:1488
DebtDirection
Definition: Steps.h:42
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
Definition: BookStep.cpp:1482
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition: PaySteps.cpp:34
WaiveTransferFee
Definition: View.h:44
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:299
@ tecINVARIANT_FAILED
Definition: TER.h:313
static std::pair< TER, std::unique_ptr< Step > > make_BookStepHelper(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1451
@ tesSUCCESS
Definition: TER.h:244
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1476
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:533
beast::Journal const j
Definition: Steps.h:562
boost::container::flat_set< Issue > & seenBookOuts
A strand may not include an offer that output the same issue more than once.
Definition: Steps.h:559
ReadView const & view
Current ReadView.
Definition: Steps.h:534
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:555
Step const *const prevStep
The previous step in the strand.
Definition: Steps.h:549
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
Definition: Steps.h:543
AMMContext & ammContext
Definition: Steps.h:560