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_)
163 boost::container::flat_set<uint256>& ofrsToRm,
170 boost::container::flat_set<uint256>& ofrsToRm,
210 return !(lhs == rhs);
221 template <
class Callback>
227 Callback& callback)
const;
230 template <
template <
typename,
typename>
typename Offer>
235 TAmounts<TIn, TOut>
const& ofrAmt,
236 TAmounts<TIn, TOut>
const& stepAmt,
237 TOut
const& ownerGives)
const;
270 template <
class TIn,
class TOut>
280 template <
template <
typename,
typename>
typename Offer>
357 template <
class TIn,
class TOut>
359 :
public BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>
371 assert(limitQuality);
373 Throw<FlowException>(
tefINTERNAL,
"Offer requires quality.");
374 return *limitQuality;
388 template <
template <
typename,
typename>
typename Offer>
396 bool const offerAttempted)
const
428 strandSrc == offer.owner() && strandDst == offer.owner())
431 if (
auto const key = offer.
key())
432 offers.permRmOffer(*key);
457 Step const* prevStep,
464 return owner == srcAcct
472 Step const* prevStep,
513 template <
class TIn,
class TOut,
class TDerived>
518 return book_ == bs->book_;
522 template <
class TIn,
class TOut,
class TDerived>
532 return {std::nullopt, dir};
534 auto const waiveFee = (std::get<OfferType>(*res) == OfferType::AMM)
538 Quality
const q =
static_cast<TDerived const*
>(
this)->adjustQualityWithFees(
539 v, std::get<Quality>(*res), prevStepDir, waiveFee);
543 template <
class TIn,
class TOut,
class TDerived>
553 return {std::nullopt, dir};
560 static_cast<TDerived const*
>(
this)->adjustQualityWithFees(
570 Quality
const q =
static_cast<TDerived const*
>(
this)->adjustQualityWithFees(
575 template <
class TIn,
class TOut,
class TDerived>
583 template <
class TIn,
class TOut,
class Offer>
587 TAmounts<TIn, TOut>& ofrAmt,
588 TAmounts<TIn, TOut>& stpAmt,
594 if (limit < stpAmt.in)
598 mulRatio(stpAmt.in, QUALITY_ONE, transferRateIn,
false);
599 ofrAmt = offer.limitIn(ofrAmt, inLmt);
600 stpAmt.out = ofrAmt.out;
602 ofrAmt.out, transferRateOut, QUALITY_ONE,
false);
607 template <
class TIn,
class TOut,
class Offer>
611 TAmounts<TIn, TOut>& ofrAmt,
612 TAmounts<TIn, TOut>& stpAmt,
619 if (limit < stpAmt.out)
623 stpAmt.out, transferRateOut, QUALITY_ONE,
false);
624 ofrAmt = offer.limitOut(
630 mulRatio(ofrAmt.in, transferRateIn, QUALITY_ONE,
true);
634 template <
class TIn,
class TOut,
class TDerived>
635 template <
class Callback>
641 Callback& callback)
const
649 if (
isXRP(
id) ||
id == this->strandDst_)
655 redeems(prevStepDir) ? rate(book_.in.account) : QUALITY_ONE;
658 ownerPaysTransferFee_ ? rate(book_.out.account) : QUALITY_ONE;
661 maxOffersToConsume_, j_);
668 bool offerAttempted =
false;
670 auto execOffer = [&](
auto& offer) {
674 ofrQ = offer.quality();
675 else if (*ofrQ != offer.quality())
678 if (
static_cast<TDerived const*
>(
this)->limitSelfCrossQuality(
679 strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
684 if (flowCross && (!
isXRP(offer.issueIn().currency)) &&
685 (offer.owner() != offer.issueIn().account))
687 auto const& issuerID = offer.issueIn().account;
692 auto const& ownerID = offer.owner();
693 auto const authFlag =
696 auto const line = afView.
read(
697 keylet::line(ownerID, issuerID, offer.issueIn().currency));
699 if (!line || (((*line)[
sfFlags] & authFlag) == 0))
703 if (
auto const key = offer.
key())
704 offers.permRmOffer(*key);
714 if (!
static_cast<TDerived const*
>(
this)->checkQualityThreshold(
718 auto const [ofrInRate, ofrOutRate] = offer.adjustRates(
719 static_cast<TDerived const*
>(
this)->getOfrInRate(
720 prevStep_, offer.owner(), trIn),
721 static_cast<TDerived const*
>(
this)->getOfrOutRate(
722 prevStep_, offer.owner(), strandDst_, trOut));
724 auto ofrAmt = offer.amount();
726 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true),
731 mulRatio(ofrAmt.out, ofrOutRate, QUALITY_ONE,
false);
733 auto const funds = offer.isFunded()
735 : offers.ownerFunds();
738 if (funds < ownerGives)
743 ownerGives, QUALITY_ONE, ofrOutRate,
false);
748 ofrAmt = offer.limitOut(
749 ofrAmt, stpAmt.out, fixReduced,
false);
752 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true);
755 offerAttempted =
true;
757 offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate);
762 auto ammOffer = getAMMOffer(sb, quality);
763 return !ammOffer || execOffer(*ammOffer);
768 if (tryAMM(offers.tip().quality()))
772 if (!execOffer(offers.tip()))
774 }
while (offers.step());
780 tryAMM(std::nullopt);
783 return {offers.permToRemove(), counter.
count()};
786 template <
class TIn,
class TOut,
class TDerived>
787 template <
template <
typename,
typename>
typename Offer>
792 TAmounts<TIn, TOut>
const& ofrAmt,
793 TAmounts<TIn, TOut>
const& stepAmt,
794 TOut
const& ownerGives)
const
799 auto const dr = offer.send(
806 Throw<FlowException>(dr);
812 auto const cr = offer.send(
819 Throw<FlowException>(cr);
822 offer.consume(sb, ofrAmt);
825 template <
class TIn,
class TOut,
class TDerived>
832 return ammLiquidity_->getOffer(view, clobQuality);
836 template <
class TIn,
class TOut,
class TDerived>
843 auto const clobQuality =
849 if (
auto const ammOffer = getAMMOffer(view, std::nullopt); ammOffer &&
850 ((clobQuality && ammOffer->quality() > clobQuality) || !clobQuality))
856 template <
class TIn,
class TOut,
class TDerived>
861 if (
auto const res = tip(view); !res)
863 else if (
auto const q = std::get_if<Quality>(&(*res)))
870 template <
class TIn,
class TOut,
class TDerived>
874 if (
auto const res = tip(view); !res)
876 else if (
auto const q = std::get_if<Quality>(&(*res)))
879 return std::get<AMMOffer<TIn, TOut>>(*res).getQualityFunc();
882 template <
class TCollection>
884 sum(TCollection
const& col)
888 return TResult{beast::zero};
892 template <
class TIn,
class TOut,
class TDerived>
897 boost::container::flat_set<uint256>& ofrsToRm,
902 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
904 auto remainingOut =
out;
906 boost::container::flat_multiset<TIn> savedIns;
907 savedIns.reserve(64);
908 boost::container::flat_multiset<TOut> savedOuts;
909 savedOuts.reserve(64);
915 auto eachOffer = [&](
auto& offer,
916 TAmounts<TIn, TOut>
const& ofrAmt,
917 TAmounts<TIn, TOut>
const& stpAmt,
918 TOut
const& ownerGives,
921 if (remainingOut <= beast::zero)
924 if (stpAmt.out <= remainingOut)
926 savedIns.insert(stpAmt.in);
927 savedOuts.insert(stpAmt.out);
928 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
929 remainingOut =
out - result.out;
930 this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
937 auto ofrAdjAmt = ofrAmt;
938 auto stpAdjAmt = stpAmt;
939 auto ownerGivesAdj = ownerGives;
949 remainingOut = beast::zero;
950 savedIns.insert(stpAdjAmt.in);
951 savedOuts.insert(remainingOut);
952 result.in =
sum(savedIns);
954 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
961 return offer.fully_consumed();
966 auto const prevStepDebtDir = [&] {
971 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
972 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
974 offersUsed_ = offersConsumed;
977 if (offersConsumed >= maxOffersToConsume_)
983 cache_.emplace(beast::zero, beast::zero);
984 return {beast::zero, beast::zero};
993 switch (remainingOut.signum())
998 <<
"BookStep remainingOut < 0 " <<
to_string(remainingOut);
1000 cache_.emplace(beast::zero, beast::zero);
1001 return {beast::zero, beast::zero};
1010 cache_.emplace(result.in, result.out);
1011 return {result.in, result.out};
1014 template <
class TIn,
class TOut,
class TDerived>
1019 boost::container::flat_set<uint256>& ofrsToRm,
1024 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
1026 auto remainingIn =
in;
1028 boost::container::flat_multiset<TIn> savedIns;
1029 savedIns.reserve(64);
1030 boost::container::flat_multiset<TOut> savedOuts;
1031 savedOuts.reserve(64);
1035 auto eachOffer = [&](
auto& offer,
1036 TAmounts<TIn, TOut>
const& ofrAmt,
1037 TAmounts<TIn, TOut>
const& stpAmt,
1038 TOut
const& ownerGives,
1043 if (remainingIn <= beast::zero)
1046 bool processMore =
true;
1047 auto ofrAdjAmt = ofrAmt;
1048 auto stpAdjAmt = stpAmt;
1049 auto ownerGivesAdj = ownerGives;
1051 typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
1052 if (stpAmt.in <= remainingIn)
1054 savedIns.insert(stpAmt.in);
1055 lastOut = savedOuts.insert(stpAmt.out);
1056 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
1070 savedIns.insert(remainingIn);
1071 lastOut = savedOuts.insert(stpAdjAmt.out);
1072 result.out =
sum(savedOuts);
1075 processMore =
false;
1078 if (result.out > cache_->out && result.in <= cache_->in)
1087 auto const lastOutAmt = *lastOut;
1088 savedOuts.erase(lastOut);
1089 auto const remainingOut = cache_->out -
sum(savedOuts);
1090 auto ofrAdjAmtRev = ofrAmt;
1091 auto stpAdjAmtRev = stpAmt;
1092 auto ownerGivesAdjRev = ownerGives;
1103 if (stpAdjAmtRev.in == remainingIn)
1106 result.out = cache_->out;
1109 savedIns.insert(result.in);
1111 savedOuts.insert(result.out);
1113 ofrAdjAmt = ofrAdjAmtRev;
1114 stpAdjAmt.in = remainingIn;
1115 stpAdjAmt.out = remainingOut;
1116 ownerGivesAdj = ownerGivesAdjRev;
1122 savedOuts.insert(lastOutAmt);
1126 remainingIn =
in - result.in;
1127 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
1133 return processMore || offer.fully_consumed();
1137 auto const prevStepDebtDir = [&] {
1142 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
1143 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
1145 offersUsed_ = offersConsumed;
1148 if (offersConsumed >= maxOffersToConsume_)
1154 cache_.emplace(beast::zero, beast::zero);
1155 return {beast::zero, beast::zero};
1164 switch (remainingIn.signum())
1169 <<
"BookStep remainingIn < 0 " <<
to_string(remainingIn);
1171 cache_.emplace(beast::zero, beast::zero);
1172 return {beast::zero, beast::zero};
1181 cache_.emplace(result.in, result.out);
1182 return {result.in, result.out};
1185 template <
class TIn,
class TOut,
class TDerived>
1194 JLOG(j_.
trace()) <<
"Expected valid cache in validFwd";
1198 auto const savCache = *cache_;
1202 boost::container::flat_set<uint256> dummy;
1203 fwdImp(sb, afView, dummy, get<TIn>(
in));
1205 catch (FlowException
const&)
1210 if (!(
checkNear(savCache.in, cache_->in) &&
1213 JLOG(j_.
warn()) <<
"Strand re-execute check failed."
1214 <<
" ExpectedIn: " <<
to_string(savCache.in)
1215 <<
" CachedIn: " <<
to_string(cache_->in)
1216 <<
" ExpectedOut: " <<
to_string(savCache.out)
1217 <<
" CachedOut: " <<
to_string(cache_->out);
1223 template <
class TIn,
class TOut,
class TDerived>
1227 if (book_.in == book_.out)
1229 JLOG(j_.
debug()) <<
"BookStep: Book with same in and out issuer "
1235 JLOG(j_.
debug()) <<
"Book: currency is inconsistent with issuer."
1245 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1251 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1255 auto issuerExists = [](
ReadView const& view,
Issue const& iss) ->
bool {
1259 if (!issuerExists(ctx.
view, book_.in) || !issuerExists(ctx.
view, book_.out))
1261 JLOG(j_.
debug()) <<
"BookStep: deleted issuer detected: " << *
this;
1269 auto const& view = ctx.
view;
1270 auto const& cur = book_.in.account;
1289 template <
class TIn,
class TOut,
class TDerived>
1294 return book == bs->book();
1303 if (inXRP && outXRP)
1308 if (inXRP && !outXRP)
1313 if (!inXRP && outXRP)
1318 if (!inXRP && !outXRP)
1329 template <
class TIn,
class TOut>
1337 auto offerCrossingStep =
1338 std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx,
in,
out);
1339 ter = offerCrossingStep->check(ctx);
1340 r = std::move(offerCrossingStep);
1345 std::make_unique<BookPaymentStep<TIn, TOut>>(ctx,
in,
out);
1346 ter = paymentStep->check(ctx);
1347 r = std::move(paymentStep);
1350 return {ter,
nullptr};
1358 return make_BookStepHelper<IOUAmount, IOUAmount>(ctx,
in,
out);
1364 return make_BookStepHelper<IOUAmount, XRPAmount>(ctx,
in,
xrpIssue());
1370 return make_BookStepHelper<XRPAmount, IOUAmount>(ctx,
xrpIssue(),
out);