20 #include <ripple/app/paths/Credit.h>
21 #include <ripple/app/paths/NodeDirectory.h>
22 #include <ripple/app/paths/impl/FlatSets.h>
23 #include <ripple/app/paths/impl/Steps.h>
24 #include <ripple/app/tx/impl/OfferStream.h>
25 #include <ripple/basics/IOUAmount.h>
26 #include <ripple/basics/Log.h>
27 #include <ripple/basics/XRPAmount.h>
28 #include <ripple/basics/contract.h>
29 #include <ripple/ledger/Directory.h>
30 #include <ripple/ledger/PaymentSandbox.h>
31 #include <ripple/protocol/Book.h>
32 #include <ripple/protocol/Feature.h>
33 #include <ripple/protocol/Quality.h>
35 #include <boost/container/flat_set.hpp>
42 template <
class TIn,
class TOut,
class TDerived>
43 class BookStep :
public StepImp<TIn, TOut, BookStep<TIn, TOut, TDerived>>
62 Cache(TIn
const& in_, TOut
const& out_) :
in(in_),
out(out_)
95 boost::optional<EitherAmount>
103 boost::optional<EitherAmount>
118 boost::optional<Book>
132 boost::container::flat_set<uint256>& ofrsToRm,
139 boost::container::flat_set<uint256>& ofrsToRm,
179 return !(lhs == rhs);
190 template <
class Callback>
196 Callback& callback)
const;
202 TAmounts<TIn, TOut>
const& ofrAmt,
203 TAmounts<TIn, TOut>
const& stepAmt,
204 TOut
const& ownerGives)
const;
216 template <
class TIn,
class TOut>
231 boost::optional<Quality>&,
300 template <
class TIn,
class TOut>
302 :
public BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>
314 assert(limitQuality);
316 Throw<FlowException>(
tefINTERNAL,
"Offer requires quality.");
317 return *limitQuality;
336 boost::optional<Quality>& ofrQ,
338 bool const offerAttempted)
const
370 strandSrc == offer.owner() && strandDst == offer.owner())
373 offers.permRmOffer(offer.
key());
398 Step const* prevStep,
407 offer.owner() == *srcAcct
415 Step const* prevStep,
422 offer.owner() == strandDst
455 template <
class TIn,
class TOut,
class TDerived>
460 return book_ == bs->book_;
464 template <
class TIn,
class TOut,
class TDerived>
476 return {boost::none, dir};
478 Quality
const q =
static_cast<TDerived const*
>(
this)->adjustQualityWithFees(
484 template <
class TIn,
class TOut>
488 TAmounts<TIn, TOut>& ofrAmt,
489 TAmounts<TIn, TOut>& stpAmt,
495 if (limit < stpAmt.in)
499 mulRatio(stpAmt.in, QUALITY_ONE, transferRateIn,
false);
500 ofrAmt = ofrQ.ceil_in(ofrAmt, inLmt);
501 stpAmt.out = ofrAmt.out;
503 ofrAmt.out, transferRateOut, QUALITY_ONE,
false);
508 template <
class TIn,
class TOut>
512 TAmounts<TIn, TOut>& ofrAmt,
513 TAmounts<TIn, TOut>& stpAmt,
519 if (limit < stpAmt.out)
523 stpAmt.out, transferRateOut, QUALITY_ONE,
false);
524 ofrAmt = ofrQ.ceil_out(ofrAmt, stpAmt.out);
526 mulRatio(ofrAmt.in, transferRateIn, QUALITY_ONE,
true);
530 template <
class TIn,
class TOut,
class TDerived>
531 template <
class Callback>
537 Callback& callback)
const
545 if (
isXRP(
id) ||
id == this->strandDst_)
551 redeems(prevStepDir) ? rate(book_.in.account) : QUALITY_ONE;
554 ownerPaysTransferFee_ ? rate(book_.out.account) : QUALITY_ONE;
557 maxOffersToConsume_, j_);
563 bool offerAttempted =
false;
564 boost::optional<Quality> ofrQ;
565 while (offers.step())
567 auto& offer = offers.tip();
572 ofrQ = offer.quality();
573 else if (*ofrQ != offer.quality())
576 if (
static_cast<TDerived const*
>(
this)->limitSelfCrossQuality(
577 strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
582 if (flowCross && (!
isXRP(offer.issueIn().currency)) &&
583 (offer.owner() != offer.issueIn().account))
585 auto const& issuerID = offer.issueIn().account;
590 auto const& ownerID = offer.owner();
591 auto const authFlag =
594 auto const line = afView.
read(
595 keylet::line(ownerID, issuerID, offer.issueIn().currency));
597 if (!line || (((*line)[
sfFlags] & authFlag) == 0))
601 offers.permRmOffer(offer.
key());
611 if (!
static_cast<TDerived const*
>(
this)->checkQualityThreshold(offer))
614 auto const ofrInRate =
static_cast<TDerived const*
>(
this)->getOfrInRate(
615 prevStep_, offer, trIn);
617 auto const ofrOutRate =
618 static_cast<TDerived const*
>(
this)->getOfrOutRate(
619 prevStep_, offer, strandDst_, trOut);
621 auto ofrAmt = offer.amount();
623 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true),
628 mulRatio(ofrAmt.out, ofrOutRate, QUALITY_ONE,
false);
630 auto const funds = (offer.owner() == offer.issueOut().account)
632 : offers.ownerFunds();
634 if (funds < ownerGives)
639 ownerGives, QUALITY_ONE, ofrOutRate,
false);
640 ofrAmt = ofrQ->ceil_out(ofrAmt, stpAmt.out);
642 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true);
645 offerAttempted =
true;
646 if (!callback(offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate))
650 return {offers.permToRemove(), counter.
count()};
653 template <
class TIn,
class TOut,
class TDerived>
658 TAmounts<TIn, TOut>
const& ofrAmt,
659 TAmounts<TIn, TOut>
const& stepAmt,
660 TOut
const& ownerGives)
const
672 Throw<FlowException>(dr);
685 Throw<FlowException>(cr);
688 offer.consume(sb, ofrAmt);
691 template <
class TCollection>
693 sum(TCollection
const& col)
697 return TResult{beast::zero};
701 template <
class TIn,
class TOut,
class TDerived>
706 boost::container::flat_set<uint256>& ofrsToRm,
711 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
713 auto remainingOut =
out;
715 boost::container::flat_multiset<TIn> savedIns;
716 savedIns.reserve(64);
717 boost::container::flat_multiset<TOut> savedOuts;
718 savedOuts.reserve(64);
725 TAmounts<TIn, TOut>
const& ofrAmt,
726 TAmounts<TIn, TOut>
const& stpAmt,
727 TOut
const& ownerGives,
730 if (remainingOut <= beast::zero)
733 if (stpAmt.out <= remainingOut)
735 savedIns.insert(stpAmt.in);
736 savedOuts.insert(stpAmt.out);
737 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
738 remainingOut =
out - result.out;
739 this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
746 auto ofrAdjAmt = ofrAmt;
747 auto stpAdjAmt = stpAmt;
748 auto ownerGivesAdj = ownerGives;
757 remainingOut = beast::zero;
758 savedIns.insert(stpAdjAmt.in);
759 savedOuts.insert(remainingOut);
760 result.in =
sum(savedIns);
762 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
769 return offer.fully_consumed();
774 auto const prevStepDebtDir = [&] {
779 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
780 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
784 if (offersConsumed >= maxOffersToConsume_)
790 cache_.emplace(beast::zero, beast::zero);
791 return {beast::zero, beast::zero};
800 switch (remainingOut.signum())
805 <<
"BookStep remainingOut < 0 " <<
to_string(remainingOut);
807 cache_.emplace(beast::zero, beast::zero);
808 return {beast::zero, beast::zero};
817 cache_.emplace(result.in, result.out);
818 return {result.in, result.out};
821 template <
class TIn,
class TOut,
class TDerived>
826 boost::container::flat_set<uint256>& ofrsToRm,
831 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
833 auto remainingIn =
in;
835 boost::container::flat_multiset<TIn> savedIns;
836 savedIns.reserve(64);
837 boost::container::flat_multiset<TOut> savedOuts;
838 savedOuts.reserve(64);
843 TAmounts<TIn, TOut>
const& ofrAmt,
844 TAmounts<TIn, TOut>
const& stpAmt,
845 TOut
const& ownerGives,
850 if (remainingIn <= beast::zero)
853 bool processMore =
true;
854 auto ofrAdjAmt = ofrAmt;
855 auto stpAdjAmt = stpAmt;
856 auto ownerGivesAdj = ownerGives;
858 typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
859 if (stpAmt.in <= remainingIn)
861 savedIns.insert(stpAmt.in);
862 lastOut = savedOuts.insert(stpAmt.out);
863 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
877 savedIns.insert(remainingIn);
878 lastOut = savedOuts.insert(stpAdjAmt.out);
879 result.out =
sum(savedOuts);
885 if (result.out > cache_->out && result.in <= cache_->in)
894 auto const lastOutAmt = *lastOut;
895 savedOuts.erase(lastOut);
896 auto const remainingOut = cache_->out -
sum(savedOuts);
897 auto ofrAdjAmtRev = ofrAmt;
898 auto stpAdjAmtRev = stpAmt;
899 auto ownerGivesAdjRev = ownerGives;
909 if (stpAdjAmtRev.in == remainingIn)
912 result.out = cache_->out;
915 savedIns.insert(result.in);
917 savedOuts.insert(result.out);
919 ofrAdjAmt = ofrAdjAmtRev;
920 stpAdjAmt.in = remainingIn;
921 stpAdjAmt.out = remainingOut;
922 ownerGivesAdj = ownerGivesAdjRev;
928 savedOuts.insert(lastOutAmt);
932 remainingIn =
in - result.in;
933 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
939 return processMore || offer.fully_consumed();
943 auto const prevStepDebtDir = [&] {
948 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
949 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
953 if (offersConsumed >= maxOffersToConsume_)
959 cache_.emplace(beast::zero, beast::zero);
960 return {beast::zero, beast::zero};
969 switch (remainingIn.signum())
974 <<
"BookStep remainingIn < 0 " <<
to_string(remainingIn);
976 cache_.emplace(beast::zero, beast::zero);
977 return {beast::zero, beast::zero};
986 cache_.emplace(result.in, result.out);
987 return {result.in, result.out};
990 template <
class TIn,
class TOut,
class TDerived>
999 JLOG(j_.
trace()) <<
"Expected valid cache in validFwd";
1003 auto const savCache = *cache_;
1007 boost::container::flat_set<uint256> dummy;
1008 fwdImp(sb, afView, dummy, get<TIn>(
in));
1010 catch (FlowException
const&)
1015 if (!(
checkNear(savCache.in, cache_->in) &&
1018 JLOG(j_.
warn()) <<
"Strand re-execute check failed."
1019 <<
" ExpectedIn: " <<
to_string(savCache.in)
1020 <<
" CachedIn: " <<
to_string(cache_->in)
1021 <<
" ExpectedOut: " <<
to_string(savCache.out)
1022 <<
" CachedOut: " <<
to_string(cache_->out);
1028 template <
class TIn,
class TOut,
class TDerived>
1032 if (book_.in == book_.out)
1034 JLOG(j_.
debug()) <<
"BookStep: Book with same in and out issuer "
1040 JLOG(j_.
debug()) <<
"Book: currency is inconsistent with issuer."
1050 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1056 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1060 auto issuerExists = [](
ReadView const& view,
Issue const& iss) ->
bool {
1064 if (!issuerExists(ctx.
view, book_.in) || !issuerExists(ctx.
view, book_.out))
1066 JLOG(j_.
debug()) <<
"BookStep: deleted issuer detected: " << *
this;
1074 auto const& view = ctx.
view;
1075 auto const& cur = book_.in.account;
1094 template <
class TIn,
class TOut,
class TDerived>
1099 return book == bs->book();
1108 if (inXRP && outXRP)
1113 if (inXRP && !outXRP)
1118 if (!inXRP && outXRP)
1123 if (!inXRP && !outXRP)
1134 template <
class TIn,
class TOut>
1142 auto offerCrossingStep =
1143 std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx,
in,
out);
1144 ter = offerCrossingStep->check(ctx);
1145 r = std::move(offerCrossingStep);
1150 std::make_unique<BookPaymentStep<TIn, TOut>>(ctx,
in,
out);
1151 ter = paymentStep->check(ctx);
1152 r = std::move(paymentStep);
1155 return {ter,
nullptr};
1163 return make_BookStepHelper<IOUAmount, IOUAmount>(ctx,
in,
out);
1169 return make_BookStepHelper<IOUAmount, XRPAmount>(ctx,
in,
xrpIssue());
1175 return make_BookStepHelper<XRPAmount, IOUAmount>(ctx,
xrpIssue(),
out);