20 #include <ripple/app/paths/Credit.h>
21 #include <ripple/app/paths/impl/FlatSets.h>
22 #include <ripple/app/paths/impl/Steps.h>
23 #include <ripple/app/tx/impl/OfferStream.h>
24 #include <ripple/basics/IOUAmount.h>
25 #include <ripple/basics/Log.h>
26 #include <ripple/basics/XRPAmount.h>
27 #include <ripple/basics/contract.h>
28 #include <ripple/ledger/Directory.h>
29 #include <ripple/ledger/PaymentSandbox.h>
30 #include <ripple/protocol/Book.h>
31 #include <ripple/protocol/Feature.h>
32 #include <ripple/protocol/Quality.h>
34 #include <boost/container/flat_set.hpp>
41 template <
class TIn,
class TOut,
class TDerived>
42 class BookStep :
public StepImp<TIn, TOut, BookStep<TIn, TOut, TDerived>>
69 Cache(TIn
const& in_, TOut
const& out_) :
in(in_),
out(out_)
142 boost::container::flat_set<uint256>& ofrsToRm,
149 boost::container::flat_set<uint256>& ofrsToRm,
189 return !(lhs == rhs);
200 template <
class Callback>
206 Callback& callback)
const;
212 TAmounts<TIn, TOut>
const& ofrAmt,
213 TAmounts<TIn, TOut>
const& stepAmt,
214 TOut
const& ownerGives)
const;
226 template <
class TIn,
class TOut>
310 template <
class TIn,
class TOut>
312 :
public BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>
324 assert(limitQuality);
326 Throw<FlowException>(
tefINTERNAL,
"Offer requires quality.");
327 return *limitQuality;
348 bool const offerAttempted)
const
380 strandSrc == offer.owner() && strandDst == offer.owner())
383 offers.permRmOffer(offer.
key());
408 Step const* prevStep,
417 offer.owner() == *srcAcct
425 Step const* prevStep,
432 offer.owner() == strandDst
465 template <
class TIn,
class TOut,
class TDerived>
470 return book_ == bs->book_;
474 template <
class TIn,
class TOut,
class TDerived>
486 return {std::nullopt, dir};
488 Quality
const q =
static_cast<TDerived const*
>(
this)->adjustQualityWithFees(
493 template <
class TIn,
class TOut,
class TDerived>
501 template <
class TIn,
class TOut>
505 TAmounts<TIn, TOut>& ofrAmt,
506 TAmounts<TIn, TOut>& stpAmt,
512 if (limit < stpAmt.in)
516 mulRatio(stpAmt.in, QUALITY_ONE, transferRateIn,
false);
517 ofrAmt = ofrQ.ceil_in(ofrAmt, inLmt);
518 stpAmt.out = ofrAmt.out;
520 ofrAmt.out, transferRateOut, QUALITY_ONE,
false);
525 template <
class TIn,
class TOut>
529 TAmounts<TIn, TOut>& ofrAmt,
530 TAmounts<TIn, TOut>& stpAmt,
537 if (limit < stpAmt.out)
541 stpAmt.out, transferRateOut, QUALITY_ONE,
false);
547 ofrAmt = ofrQ.ceil_out_strict(ofrAmt, stpAmt.out,
true);
549 ofrAmt = ofrQ.ceil_out(ofrAmt, stpAmt.out);
551 mulRatio(ofrAmt.in, transferRateIn, QUALITY_ONE,
true);
555 template <
class TIn,
class TOut,
class TDerived>
556 template <
class Callback>
562 Callback& callback)
const
570 if (
isXRP(
id) ||
id == this->strandDst_)
576 redeems(prevStepDir) ? rate(book_.in.account) : QUALITY_ONE;
579 ownerPaysTransferFee_ ? rate(book_.out.account) : QUALITY_ONE;
582 maxOffersToConsume_, j_);
589 bool offerAttempted =
false;
591 while (offers.step())
593 auto& offer = offers.tip();
598 ofrQ = offer.quality();
599 else if (*ofrQ != offer.quality())
602 if (
static_cast<TDerived const*
>(
this)->limitSelfCrossQuality(
603 strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
608 if (flowCross && (!
isXRP(offer.issueIn().currency)) &&
609 (offer.owner() != offer.issueIn().account))
611 auto const& issuerID = offer.issueIn().account;
616 auto const& ownerID = offer.owner();
617 auto const authFlag =
620 auto const line = afView.
read(
621 keylet::line(ownerID, issuerID, offer.issueIn().currency));
623 if (!line || (((*line)[
sfFlags] & authFlag) == 0))
627 offers.permRmOffer(offer.
key());
637 if (!
static_cast<TDerived const*
>(
this)->checkQualityThreshold(offer))
640 auto const ofrInRate =
static_cast<TDerived const*
>(
this)->getOfrInRate(
641 prevStep_, offer, trIn);
643 auto const ofrOutRate =
644 static_cast<TDerived const*
>(
this)->getOfrOutRate(
645 prevStep_, offer, strandDst_, trOut);
647 auto ofrAmt = offer.amount();
649 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true),
654 mulRatio(ofrAmt.out, ofrOutRate, QUALITY_ONE,
false);
656 auto const funds = (offer.owner() == offer.issueOut().account)
658 : offers.ownerFunds();
660 if (funds < ownerGives)
665 ownerGives, QUALITY_ONE, ofrOutRate,
false);
671 ofrAmt = ofrQ->ceil_out_strict(
672 ofrAmt, stpAmt.out,
false);
674 ofrAmt = ofrQ->ceil_out(ofrAmt, stpAmt.out);
677 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true);
680 offerAttempted =
true;
681 if (!callback(offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate))
685 return {offers.permToRemove(), counter.
count()};
688 template <
class TIn,
class TOut,
class TDerived>
693 TAmounts<TIn, TOut>
const& ofrAmt,
694 TAmounts<TIn, TOut>
const& stepAmt,
695 TOut
const& ownerGives)
const
707 Throw<FlowException>(dr);
720 Throw<FlowException>(cr);
723 offer.consume(sb, ofrAmt);
726 template <
class TCollection>
728 sum(TCollection
const& col)
732 return TResult{beast::zero};
736 template <
class TIn,
class TOut,
class TDerived>
741 boost::container::flat_set<uint256>& ofrsToRm,
746 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
748 auto remainingOut =
out;
750 boost::container::flat_multiset<TIn> savedIns;
751 savedIns.reserve(64);
752 boost::container::flat_multiset<TOut> savedOuts;
753 savedOuts.reserve(64);
760 TAmounts<TIn, TOut>
const& ofrAmt,
761 TAmounts<TIn, TOut>
const& stpAmt,
762 TOut
const& ownerGives,
765 if (remainingOut <= beast::zero)
768 if (stpAmt.out <= remainingOut)
770 savedIns.insert(stpAmt.in);
771 savedOuts.insert(stpAmt.out);
772 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
773 remainingOut =
out - result.out;
774 this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
781 auto ofrAdjAmt = ofrAmt;
782 auto stpAdjAmt = stpAmt;
783 auto ownerGivesAdj = ownerGives;
793 remainingOut = beast::zero;
794 savedIns.insert(stpAdjAmt.in);
795 savedOuts.insert(remainingOut);
796 result.in =
sum(savedIns);
798 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
805 return offer.fully_consumed();
810 auto const prevStepDebtDir = [&] {
815 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
816 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
818 offersUsed_ = offersConsumed;
821 if (offersConsumed >= maxOffersToConsume_)
827 cache_.emplace(beast::zero, beast::zero);
828 return {beast::zero, beast::zero};
837 switch (remainingOut.signum())
842 <<
"BookStep remainingOut < 0 " <<
to_string(remainingOut);
844 cache_.emplace(beast::zero, beast::zero);
845 return {beast::zero, beast::zero};
854 cache_.emplace(result.in, result.out);
855 return {result.in, result.out};
858 template <
class TIn,
class TOut,
class TDerived>
863 boost::container::flat_set<uint256>& ofrsToRm,
868 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
870 auto remainingIn =
in;
872 boost::container::flat_multiset<TIn> savedIns;
873 savedIns.reserve(64);
874 boost::container::flat_multiset<TOut> savedOuts;
875 savedOuts.reserve(64);
880 TAmounts<TIn, TOut>
const& ofrAmt,
881 TAmounts<TIn, TOut>
const& stpAmt,
882 TOut
const& ownerGives,
887 if (remainingIn <= beast::zero)
890 bool processMore =
true;
891 auto ofrAdjAmt = ofrAmt;
892 auto stpAdjAmt = stpAmt;
893 auto ownerGivesAdj = ownerGives;
895 typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
896 if (stpAmt.in <= remainingIn)
898 savedIns.insert(stpAmt.in);
899 lastOut = savedOuts.insert(stpAmt.out);
900 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
914 savedIns.insert(remainingIn);
915 lastOut = savedOuts.insert(stpAdjAmt.out);
916 result.out =
sum(savedOuts);
922 if (result.out > cache_->out && result.in <= cache_->in)
931 auto const lastOutAmt = *lastOut;
932 savedOuts.erase(lastOut);
933 auto const remainingOut = cache_->out -
sum(savedOuts);
934 auto ofrAdjAmtRev = ofrAmt;
935 auto stpAdjAmtRev = stpAmt;
936 auto ownerGivesAdjRev = ownerGives;
947 if (stpAdjAmtRev.in == remainingIn)
950 result.out = cache_->out;
953 savedIns.insert(result.in);
955 savedOuts.insert(result.out);
957 ofrAdjAmt = ofrAdjAmtRev;
958 stpAdjAmt.in = remainingIn;
959 stpAdjAmt.out = remainingOut;
960 ownerGivesAdj = ownerGivesAdjRev;
966 savedOuts.insert(lastOutAmt);
970 remainingIn =
in - result.in;
971 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
977 return processMore || offer.fully_consumed();
981 auto const prevStepDebtDir = [&] {
986 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
987 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
989 offersUsed_ = offersConsumed;
992 if (offersConsumed >= maxOffersToConsume_)
998 cache_.emplace(beast::zero, beast::zero);
999 return {beast::zero, beast::zero};
1008 switch (remainingIn.signum())
1013 <<
"BookStep remainingIn < 0 " <<
to_string(remainingIn);
1015 cache_.emplace(beast::zero, beast::zero);
1016 return {beast::zero, beast::zero};
1025 cache_.emplace(result.in, result.out);
1026 return {result.in, result.out};
1029 template <
class TIn,
class TOut,
class TDerived>
1038 JLOG(j_.
trace()) <<
"Expected valid cache in validFwd";
1042 auto const savCache = *cache_;
1046 boost::container::flat_set<uint256> dummy;
1047 fwdImp(sb, afView, dummy, get<TIn>(
in));
1049 catch (FlowException
const&)
1054 if (!(
checkNear(savCache.in, cache_->in) &&
1057 JLOG(j_.
warn()) <<
"Strand re-execute check failed."
1058 <<
" ExpectedIn: " <<
to_string(savCache.in)
1059 <<
" CachedIn: " <<
to_string(cache_->in)
1060 <<
" ExpectedOut: " <<
to_string(savCache.out)
1061 <<
" CachedOut: " <<
to_string(cache_->out);
1067 template <
class TIn,
class TOut,
class TDerived>
1071 if (book_.in == book_.out)
1073 JLOG(j_.
debug()) <<
"BookStep: Book with same in and out issuer "
1079 JLOG(j_.
debug()) <<
"Book: currency is inconsistent with issuer."
1089 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1095 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1099 auto issuerExists = [](
ReadView const& view,
Issue const& iss) ->
bool {
1103 if (!issuerExists(ctx.
view, book_.in) || !issuerExists(ctx.
view, book_.out))
1105 JLOG(j_.
debug()) <<
"BookStep: deleted issuer detected: " << *
this;
1113 auto const& view = ctx.
view;
1114 auto const& cur = book_.in.account;
1133 template <
class TIn,
class TOut,
class TDerived>
1138 return book == bs->book();
1147 if (inXRP && outXRP)
1152 if (inXRP && !outXRP)
1157 if (!inXRP && outXRP)
1162 if (!inXRP && !outXRP)
1173 template <
class TIn,
class TOut>
1181 auto offerCrossingStep =
1182 std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx,
in,
out);
1183 ter = offerCrossingStep->check(ctx);
1184 r = std::move(offerCrossingStep);
1189 std::make_unique<BookPaymentStep<TIn, TOut>>(ctx,
in,
out);
1190 ter = paymentStep->check(ctx);
1191 r = std::move(paymentStep);
1194 return {ter,
nullptr};
1202 return make_BookStepHelper<IOUAmount, IOUAmount>(ctx,
in,
out);
1208 return make_BookStepHelper<IOUAmount, XRPAmount>(ctx,
in,
xrpIssue());
1214 return make_BookStepHelper<XRPAmount, IOUAmount>(ctx,
xrpIssue(),
out);