20 #include <ripple/app/misc/AMMUtils.h>
21 #include <ripple/app/paths/AMMLiquidity.h>
22 #include <ripple/app/paths/AMMOffer.h>
23 #include <ripple/app/paths/impl/FlatSets.h>
24 #include <ripple/app/paths/impl/Steps.h>
25 #include <ripple/app/tx/impl/OfferStream.h>
26 #include <ripple/basics/IOUAmount.h>
27 #include <ripple/basics/Log.h>
28 #include <ripple/basics/XRPAmount.h>
29 #include <ripple/basics/contract.h>
30 #include <ripple/ledger/Directory.h>
31 #include <ripple/ledger/PaymentSandbox.h>
32 #include <ripple/protocol/Book.h>
33 #include <ripple/protocol/Feature.h>
34 #include <ripple/protocol/Quality.h>
36 #include <boost/container/flat_set.hpp>
43 template <
class TIn,
class TOut,
class TDerived>
44 class BookStep :
public StepImp<TIn, TOut, BookStep<TIn, TOut, TDerived>>
77 Cache(TIn
const& in_, TOut
const& out_) :
in(in_),
out(out_)
162 boost::container::flat_set<uint256>& ofrsToRm,
169 boost::container::flat_set<uint256>& ofrsToRm,
209 return !(lhs == rhs);
220 template <
class Callback>
226 Callback& callback)
const;
229 template <
template <
typename,
typename>
typename Offer>
234 TAmounts<TIn, TOut>
const& ofrAmt,
235 TAmounts<TIn, TOut>
const& stepAmt,
236 TOut
const& ownerGives)
const;
269 template <
class TIn,
class TOut>
279 template <
template <
typename,
typename>
typename Offer>
356 template <
class TIn,
class TOut>
358 :
public BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>
370 assert(limitQuality);
372 Throw<FlowException>(
tefINTERNAL,
"Offer requires quality.");
373 return *limitQuality;
387 template <
template <
typename,
typename>
typename Offer>
395 bool const offerAttempted)
const
427 strandSrc == offer.owner() && strandDst == offer.owner())
430 if (
auto const key = offer.
key())
431 offers.permRmOffer(*key);
456 Step const* prevStep,
463 return owner == srcAcct
471 Step const* prevStep,
512 template <
class TIn,
class TOut,
class TDerived>
517 return book_ == bs->book_;
521 template <
class TIn,
class TOut,
class TDerived>
531 return {std::nullopt, dir};
533 auto const waiveFee = (std::get<OfferType>(*res) == OfferType::AMM)
537 Quality
const q =
static_cast<TDerived const*
>(
this)->adjustQualityWithFees(
538 v, std::get<Quality>(*res), prevStepDir, waiveFee);
542 template <
class TIn,
class TOut,
class TDerived>
552 return {std::nullopt, dir};
559 static_cast<TDerived const*
>(
this)->adjustQualityWithFees(
569 Quality
const q =
static_cast<TDerived const*
>(
this)->adjustQualityWithFees(
574 template <
class TIn,
class TOut,
class TDerived>
582 template <
class TIn,
class TOut,
class Offer>
586 TAmounts<TIn, TOut>& ofrAmt,
587 TAmounts<TIn, TOut>& stpAmt,
593 if (limit < stpAmt.in)
597 mulRatio(stpAmt.in, QUALITY_ONE, transferRateIn,
false);
598 ofrAmt = offer.limitIn(ofrAmt, inLmt);
599 stpAmt.out = ofrAmt.out;
601 ofrAmt.out, transferRateOut, QUALITY_ONE,
false);
606 template <
class TIn,
class TOut,
class Offer>
610 TAmounts<TIn, TOut>& ofrAmt,
611 TAmounts<TIn, TOut>& stpAmt,
618 if (limit < stpAmt.out)
622 stpAmt.out, transferRateOut, QUALITY_ONE,
false);
623 ofrAmt = offer.limitOut(
629 mulRatio(ofrAmt.in, transferRateIn, QUALITY_ONE,
true);
633 template <
class TIn,
class TOut,
class TDerived>
634 template <
class Callback>
640 Callback& callback)
const
648 if (
isXRP(
id) ||
id == this->strandDst_)
654 redeems(prevStepDir) ? rate(book_.in.account) : QUALITY_ONE;
657 ownerPaysTransferFee_ ? rate(book_.out.account) : QUALITY_ONE;
660 maxOffersToConsume_, j_);
667 bool offerAttempted =
false;
669 auto execOffer = [&](
auto& offer) {
673 ofrQ = offer.quality();
674 else if (*ofrQ != offer.quality())
677 if (
static_cast<TDerived const*
>(
this)->limitSelfCrossQuality(
678 strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
683 if (flowCross && (!
isXRP(offer.issueIn().currency)) &&
684 (offer.owner() != offer.issueIn().account))
686 auto const& issuerID = offer.issueIn().account;
691 auto const& ownerID = offer.owner();
692 auto const authFlag =
695 auto const line = afView.
read(
696 keylet::line(ownerID, issuerID, offer.issueIn().currency));
698 if (!line || (((*line)[
sfFlags] & authFlag) == 0))
702 if (
auto const key = offer.
key())
703 offers.permRmOffer(*key);
713 if (!
static_cast<TDerived const*
>(
this)->checkQualityThreshold(
717 auto const [ofrInRate, ofrOutRate] = offer.adjustRates(
718 static_cast<TDerived const*
>(
this)->getOfrInRate(
719 prevStep_, offer.owner(), trIn),
720 static_cast<TDerived const*
>(
this)->getOfrOutRate(
721 prevStep_, offer.owner(), strandDst_, trOut));
723 auto ofrAmt = offer.amount();
725 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true),
730 mulRatio(ofrAmt.out, ofrOutRate, QUALITY_ONE,
false);
732 auto const funds = offer.isFunded()
734 : offers.ownerFunds();
737 if (funds < ownerGives)
742 ownerGives, QUALITY_ONE, ofrOutRate,
false);
747 ofrAmt = offer.limitOut(
748 ofrAmt, stpAmt.out, fixReduced,
false);
751 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true);
754 offerAttempted =
true;
756 offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate);
761 auto ammOffer = getAMMOffer(sb, quality);
762 return !ammOffer || execOffer(*ammOffer);
767 if (tryAMM(offers.tip().quality()))
771 if (!execOffer(offers.tip()))
773 }
while (offers.step());
779 tryAMM(std::nullopt);
782 return {offers.permToRemove(), counter.
count()};
785 template <
class TIn,
class TOut,
class TDerived>
786 template <
template <
typename,
typename>
typename Offer>
791 TAmounts<TIn, TOut>
const& ofrAmt,
792 TAmounts<TIn, TOut>
const& stepAmt,
793 TOut
const& ownerGives)
const
798 auto const dr = offer.send(
805 Throw<FlowException>(dr);
811 auto const cr = offer.send(
818 Throw<FlowException>(cr);
821 offer.consume(sb, ofrAmt);
824 template <
class TIn,
class TOut,
class TDerived>
831 return ammLiquidity_->getOffer(view, clobQuality);
835 template <
class TIn,
class TOut,
class TDerived>
842 auto const clobQuality =
848 if (
auto const ammOffer = getAMMOffer(view, std::nullopt); ammOffer &&
849 ((clobQuality && ammOffer->quality() > clobQuality) || !clobQuality))
855 template <
class TIn,
class TOut,
class TDerived>
860 if (
auto const res = tip(view); !res)
862 else if (
auto const q = std::get_if<Quality>(&(*res)))
869 template <
class TIn,
class TOut,
class TDerived>
873 if (
auto const res = tip(view); !res)
875 else if (
auto const q = std::get_if<Quality>(&(*res)))
878 return std::get<AMMOffer<TIn, TOut>>(*res).getQualityFunc();
881 template <
class TCollection>
883 sum(TCollection
const& col)
887 return TResult{beast::zero};
891 template <
class TIn,
class TOut,
class TDerived>
896 boost::container::flat_set<uint256>& ofrsToRm,
901 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
903 auto remainingOut =
out;
905 boost::container::flat_multiset<TIn> savedIns;
906 savedIns.reserve(64);
907 boost::container::flat_multiset<TOut> savedOuts;
908 savedOuts.reserve(64);
914 auto eachOffer = [&](
auto& offer,
915 TAmounts<TIn, TOut>
const& ofrAmt,
916 TAmounts<TIn, TOut>
const& stpAmt,
917 TOut
const& ownerGives,
920 if (remainingOut <= beast::zero)
923 if (stpAmt.out <= remainingOut)
925 savedIns.insert(stpAmt.in);
926 savedOuts.insert(stpAmt.out);
927 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
928 remainingOut =
out - result.out;
929 this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
936 auto ofrAdjAmt = ofrAmt;
937 auto stpAdjAmt = stpAmt;
938 auto ownerGivesAdj = ownerGives;
948 remainingOut = beast::zero;
949 savedIns.insert(stpAdjAmt.in);
950 savedOuts.insert(remainingOut);
951 result.in =
sum(savedIns);
953 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
960 return offer.fully_consumed();
965 auto const prevStepDebtDir = [&] {
970 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
971 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
973 offersUsed_ = offersConsumed;
976 if (offersConsumed >= maxOffersToConsume_)
982 cache_.emplace(beast::zero, beast::zero);
983 return {beast::zero, beast::zero};
992 switch (remainingOut.signum())
997 <<
"BookStep remainingOut < 0 " <<
to_string(remainingOut);
999 cache_.emplace(beast::zero, beast::zero);
1000 return {beast::zero, beast::zero};
1009 cache_.emplace(result.in, result.out);
1010 return {result.in, result.out};
1013 template <
class TIn,
class TOut,
class TDerived>
1018 boost::container::flat_set<uint256>& ofrsToRm,
1023 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
1025 auto remainingIn =
in;
1027 boost::container::flat_multiset<TIn> savedIns;
1028 savedIns.reserve(64);
1029 boost::container::flat_multiset<TOut> savedOuts;
1030 savedOuts.reserve(64);
1034 auto eachOffer = [&](
auto& offer,
1035 TAmounts<TIn, TOut>
const& ofrAmt,
1036 TAmounts<TIn, TOut>
const& stpAmt,
1037 TOut
const& ownerGives,
1042 if (remainingIn <= beast::zero)
1045 bool processMore =
true;
1046 auto ofrAdjAmt = ofrAmt;
1047 auto stpAdjAmt = stpAmt;
1048 auto ownerGivesAdj = ownerGives;
1050 typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
1051 if (stpAmt.in <= remainingIn)
1053 savedIns.insert(stpAmt.in);
1054 lastOut = savedOuts.insert(stpAmt.out);
1055 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
1069 savedIns.insert(remainingIn);
1070 lastOut = savedOuts.insert(stpAdjAmt.out);
1071 result.out =
sum(savedOuts);
1074 processMore =
false;
1077 if (result.out > cache_->out && result.in <= cache_->in)
1086 auto const lastOutAmt = *lastOut;
1087 savedOuts.erase(lastOut);
1088 auto const remainingOut = cache_->out -
sum(savedOuts);
1089 auto ofrAdjAmtRev = ofrAmt;
1090 auto stpAdjAmtRev = stpAmt;
1091 auto ownerGivesAdjRev = ownerGives;
1102 if (stpAdjAmtRev.in == remainingIn)
1105 result.out = cache_->out;
1108 savedIns.insert(result.in);
1110 savedOuts.insert(result.out);
1112 ofrAdjAmt = ofrAdjAmtRev;
1113 stpAdjAmt.in = remainingIn;
1114 stpAdjAmt.out = remainingOut;
1115 ownerGivesAdj = ownerGivesAdjRev;
1121 savedOuts.insert(lastOutAmt);
1125 remainingIn =
in - result.in;
1126 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
1132 return processMore || offer.fully_consumed();
1136 auto const prevStepDebtDir = [&] {
1141 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
1142 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
1144 offersUsed_ = offersConsumed;
1147 if (offersConsumed >= maxOffersToConsume_)
1153 cache_.emplace(beast::zero, beast::zero);
1154 return {beast::zero, beast::zero};
1163 switch (remainingIn.signum())
1168 <<
"BookStep remainingIn < 0 " <<
to_string(remainingIn);
1170 cache_.emplace(beast::zero, beast::zero);
1171 return {beast::zero, beast::zero};
1180 cache_.emplace(result.in, result.out);
1181 return {result.in, result.out};
1184 template <
class TIn,
class TOut,
class TDerived>
1193 JLOG(j_.
trace()) <<
"Expected valid cache in validFwd";
1197 auto const savCache = *cache_;
1201 boost::container::flat_set<uint256> dummy;
1202 fwdImp(sb, afView, dummy, get<TIn>(
in));
1204 catch (FlowException
const&)
1209 if (!(
checkNear(savCache.in, cache_->in) &&
1212 JLOG(j_.
warn()) <<
"Strand re-execute check failed."
1213 <<
" ExpectedIn: " <<
to_string(savCache.in)
1214 <<
" CachedIn: " <<
to_string(cache_->in)
1215 <<
" ExpectedOut: " <<
to_string(savCache.out)
1216 <<
" CachedOut: " <<
to_string(cache_->out);
1222 template <
class TIn,
class TOut,
class TDerived>
1226 if (book_.in == book_.out)
1228 JLOG(j_.
debug()) <<
"BookStep: Book with same in and out issuer "
1234 JLOG(j_.
debug()) <<
"Book: currency is inconsistent with issuer."
1244 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1250 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1254 auto issuerExists = [](
ReadView const& view,
Issue const& iss) ->
bool {
1258 if (!issuerExists(ctx.
view, book_.in) || !issuerExists(ctx.
view, book_.out))
1260 JLOG(j_.
debug()) <<
"BookStep: deleted issuer detected: " << *
this;
1268 auto const& view = ctx.
view;
1269 auto const& cur = book_.in.account;
1288 template <
class TIn,
class TOut,
class TDerived>
1293 return book == bs->book();
1302 if (inXRP && outXRP)
1307 if (inXRP && !outXRP)
1312 if (!inXRP && outXRP)
1317 if (!inXRP && !outXRP)
1328 template <
class TIn,
class TOut>
1336 auto offerCrossingStep =
1337 std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx,
in,
out);
1338 ter = offerCrossingStep->check(ctx);
1339 r = std::move(offerCrossingStep);
1344 std::make_unique<BookPaymentStep<TIn, TOut>>(ctx,
in,
out);
1345 ter = paymentStep->check(ctx);
1346 r = std::move(paymentStep);
1349 return {ter,
nullptr};
1357 return make_BookStepHelper<IOUAmount, IOUAmount>(ctx,
in,
out);
1363 return make_BookStepHelper<IOUAmount, XRPAmount>(ctx,
in,
xrpIssue());
1369 return make_BookStepHelper<XRPAmount, IOUAmount>(ctx,
xrpIssue(),
out);