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>>
70 Cache(TIn
const& in_, TOut
const& out_) :
in(in_),
out(out_)
103 boost::optional<EitherAmount>
111 boost::optional<EitherAmount>
126 boost::optional<Book>
143 boost::container::flat_set<uint256>& ofrsToRm,
150 boost::container::flat_set<uint256>& ofrsToRm,
190 return !(lhs == rhs);
201 template <
class Callback>
207 Callback& callback)
const;
213 TAmounts<TIn, TOut>
const& ofrAmt,
214 TAmounts<TIn, TOut>
const& stepAmt,
215 TOut
const& ownerGives)
const;
227 template <
class TIn,
class TOut>
242 boost::optional<Quality>&,
311 template <
class TIn,
class TOut>
313 :
public BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>
325 assert(limitQuality);
327 Throw<FlowException>(
tefINTERNAL,
"Offer requires quality.");
328 return *limitQuality;
347 boost::optional<Quality>& ofrQ,
349 bool const offerAttempted)
const
381 strandSrc == offer.owner() && strandDst == offer.owner())
384 offers.permRmOffer(offer.
key());
409 Step const* prevStep,
418 offer.owner() == *srcAcct
426 Step const* prevStep,
433 offer.owner() == strandDst
466 template <
class TIn,
class TOut,
class TDerived>
471 return book_ == bs->book_;
475 template <
class TIn,
class TOut,
class TDerived>
487 return {boost::none, dir};
489 Quality
const q =
static_cast<TDerived const*
>(
this)->adjustQualityWithFees(
494 template <
class TIn,
class TOut,
class TDerived>
502 template <
class TIn,
class TOut>
506 TAmounts<TIn, TOut>& ofrAmt,
507 TAmounts<TIn, TOut>& stpAmt,
513 if (limit < stpAmt.in)
517 mulRatio(stpAmt.in, QUALITY_ONE, transferRateIn,
false);
518 ofrAmt = ofrQ.ceil_in(ofrAmt, inLmt);
519 stpAmt.out = ofrAmt.out;
521 ofrAmt.out, transferRateOut, QUALITY_ONE,
false);
526 template <
class TIn,
class TOut>
530 TAmounts<TIn, TOut>& ofrAmt,
531 TAmounts<TIn, TOut>& stpAmt,
537 if (limit < stpAmt.out)
541 stpAmt.out, transferRateOut, QUALITY_ONE,
false);
542 ofrAmt = ofrQ.ceil_out(ofrAmt, stpAmt.out);
544 mulRatio(ofrAmt.in, transferRateIn, QUALITY_ONE,
true);
548 template <
class TIn,
class TOut,
class TDerived>
549 template <
class Callback>
555 Callback& callback)
const
563 if (
isXRP(
id) ||
id == this->strandDst_)
569 redeems(prevStepDir) ? rate(book_.in.account) : QUALITY_ONE;
572 ownerPaysTransferFee_ ? rate(book_.out.account) : QUALITY_ONE;
575 maxOffersToConsume_, j_);
581 bool offerAttempted =
false;
582 boost::optional<Quality> ofrQ;
583 while (offers.step())
585 auto& offer = offers.tip();
590 ofrQ = offer.quality();
591 else if (*ofrQ != offer.quality())
594 if (
static_cast<TDerived const*
>(
this)->limitSelfCrossQuality(
595 strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
600 if (flowCross && (!
isXRP(offer.issueIn().currency)) &&
601 (offer.owner() != offer.issueIn().account))
603 auto const& issuerID = offer.issueIn().account;
608 auto const& ownerID = offer.owner();
609 auto const authFlag =
612 auto const line = afView.
read(
613 keylet::line(ownerID, issuerID, offer.issueIn().currency));
615 if (!line || (((*line)[
sfFlags] & authFlag) == 0))
619 offers.permRmOffer(offer.
key());
629 if (!
static_cast<TDerived const*
>(
this)->checkQualityThreshold(offer))
632 auto const ofrInRate =
static_cast<TDerived const*
>(
this)->getOfrInRate(
633 prevStep_, offer, trIn);
635 auto const ofrOutRate =
636 static_cast<TDerived const*
>(
this)->getOfrOutRate(
637 prevStep_, offer, strandDst_, trOut);
639 auto ofrAmt = offer.amount();
641 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true),
646 mulRatio(ofrAmt.out, ofrOutRate, QUALITY_ONE,
false);
648 auto const funds = (offer.owner() == offer.issueOut().account)
650 : offers.ownerFunds();
652 if (funds < ownerGives)
657 ownerGives, QUALITY_ONE, ofrOutRate,
false);
658 ofrAmt = ofrQ->ceil_out(ofrAmt, stpAmt.out);
660 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true);
663 offerAttempted =
true;
664 if (!callback(offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate))
668 return {offers.permToRemove(), counter.
count()};
671 template <
class TIn,
class TOut,
class TDerived>
676 TAmounts<TIn, TOut>
const& ofrAmt,
677 TAmounts<TIn, TOut>
const& stepAmt,
678 TOut
const& ownerGives)
const
690 Throw<FlowException>(dr);
703 Throw<FlowException>(cr);
706 offer.consume(sb, ofrAmt);
709 template <
class TCollection>
711 sum(TCollection
const& col)
715 return TResult{beast::zero};
719 template <
class TIn,
class TOut,
class TDerived>
724 boost::container::flat_set<uint256>& ofrsToRm,
729 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
731 auto remainingOut =
out;
733 boost::container::flat_multiset<TIn> savedIns;
734 savedIns.reserve(64);
735 boost::container::flat_multiset<TOut> savedOuts;
736 savedOuts.reserve(64);
743 TAmounts<TIn, TOut>
const& ofrAmt,
744 TAmounts<TIn, TOut>
const& stpAmt,
745 TOut
const& ownerGives,
748 if (remainingOut <= beast::zero)
751 if (stpAmt.out <= remainingOut)
753 savedIns.insert(stpAmt.in);
754 savedOuts.insert(stpAmt.out);
755 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
756 remainingOut =
out - result.out;
757 this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
764 auto ofrAdjAmt = ofrAmt;
765 auto stpAdjAmt = stpAmt;
766 auto ownerGivesAdj = ownerGives;
775 remainingOut = beast::zero;
776 savedIns.insert(stpAdjAmt.in);
777 savedOuts.insert(remainingOut);
778 result.in =
sum(savedIns);
780 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
787 return offer.fully_consumed();
792 auto const prevStepDebtDir = [&] {
797 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
798 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
800 offersUsed_ = offersConsumed;
803 if (offersConsumed >= maxOffersToConsume_)
809 cache_.emplace(beast::zero, beast::zero);
810 return {beast::zero, beast::zero};
819 switch (remainingOut.signum())
824 <<
"BookStep remainingOut < 0 " <<
to_string(remainingOut);
826 cache_.emplace(beast::zero, beast::zero);
827 return {beast::zero, beast::zero};
836 cache_.emplace(result.in, result.out);
837 return {result.in, result.out};
840 template <
class TIn,
class TOut,
class TDerived>
845 boost::container::flat_set<uint256>& ofrsToRm,
850 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
852 auto remainingIn =
in;
854 boost::container::flat_multiset<TIn> savedIns;
855 savedIns.reserve(64);
856 boost::container::flat_multiset<TOut> savedOuts;
857 savedOuts.reserve(64);
862 TAmounts<TIn, TOut>
const& ofrAmt,
863 TAmounts<TIn, TOut>
const& stpAmt,
864 TOut
const& ownerGives,
869 if (remainingIn <= beast::zero)
872 bool processMore =
true;
873 auto ofrAdjAmt = ofrAmt;
874 auto stpAdjAmt = stpAmt;
875 auto ownerGivesAdj = ownerGives;
877 typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
878 if (stpAmt.in <= remainingIn)
880 savedIns.insert(stpAmt.in);
881 lastOut = savedOuts.insert(stpAmt.out);
882 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
896 savedIns.insert(remainingIn);
897 lastOut = savedOuts.insert(stpAdjAmt.out);
898 result.out =
sum(savedOuts);
904 if (result.out > cache_->out && result.in <= cache_->in)
913 auto const lastOutAmt = *lastOut;
914 savedOuts.erase(lastOut);
915 auto const remainingOut = cache_->out -
sum(savedOuts);
916 auto ofrAdjAmtRev = ofrAmt;
917 auto stpAdjAmtRev = stpAmt;
918 auto ownerGivesAdjRev = ownerGives;
928 if (stpAdjAmtRev.in == remainingIn)
931 result.out = cache_->out;
934 savedIns.insert(result.in);
936 savedOuts.insert(result.out);
938 ofrAdjAmt = ofrAdjAmtRev;
939 stpAdjAmt.in = remainingIn;
940 stpAdjAmt.out = remainingOut;
941 ownerGivesAdj = ownerGivesAdjRev;
947 savedOuts.insert(lastOutAmt);
951 remainingIn =
in - result.in;
952 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
958 return processMore || offer.fully_consumed();
962 auto const prevStepDebtDir = [&] {
967 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
968 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
970 offersUsed_ = offersConsumed;
973 if (offersConsumed >= maxOffersToConsume_)
979 cache_.emplace(beast::zero, beast::zero);
980 return {beast::zero, beast::zero};
989 switch (remainingIn.signum())
994 <<
"BookStep remainingIn < 0 " <<
to_string(remainingIn);
996 cache_.emplace(beast::zero, beast::zero);
997 return {beast::zero, beast::zero};
1006 cache_.emplace(result.in, result.out);
1007 return {result.in, result.out};
1010 template <
class TIn,
class TOut,
class TDerived>
1019 JLOG(j_.
trace()) <<
"Expected valid cache in validFwd";
1023 auto const savCache = *cache_;
1027 boost::container::flat_set<uint256> dummy;
1028 fwdImp(sb, afView, dummy, get<TIn>(
in));
1030 catch (FlowException
const&)
1035 if (!(
checkNear(savCache.in, cache_->in) &&
1038 JLOG(j_.
warn()) <<
"Strand re-execute check failed."
1039 <<
" ExpectedIn: " <<
to_string(savCache.in)
1040 <<
" CachedIn: " <<
to_string(cache_->in)
1041 <<
" ExpectedOut: " <<
to_string(savCache.out)
1042 <<
" CachedOut: " <<
to_string(cache_->out);
1048 template <
class TIn,
class TOut,
class TDerived>
1052 if (book_.in == book_.out)
1054 JLOG(j_.
debug()) <<
"BookStep: Book with same in and out issuer "
1060 JLOG(j_.
debug()) <<
"Book: currency is inconsistent with issuer."
1070 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1076 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1080 auto issuerExists = [](
ReadView const& view,
Issue const& iss) ->
bool {
1084 if (!issuerExists(ctx.
view, book_.in) || !issuerExists(ctx.
view, book_.out))
1086 JLOG(j_.
debug()) <<
"BookStep: deleted issuer detected: " << *
this;
1094 auto const& view = ctx.
view;
1095 auto const& cur = book_.in.account;
1114 template <
class TIn,
class TOut,
class TDerived>
1119 return book == bs->book();
1128 if (inXRP && outXRP)
1133 if (inXRP && !outXRP)
1138 if (!inXRP && outXRP)
1143 if (!inXRP && !outXRP)
1154 template <
class TIn,
class TOut>
1162 auto offerCrossingStep =
1163 std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx,
in,
out);
1164 ter = offerCrossingStep->check(ctx);
1165 r = std::move(offerCrossingStep);
1170 std::make_unique<BookPaymentStep<TIn, TOut>>(ctx,
in,
out);
1171 ter = paymentStep->check(ctx);
1172 r = std::move(paymentStep);
1175 return {ter,
nullptr};
1183 return make_BookStepHelper<IOUAmount, IOUAmount>(ctx,
in,
out);
1189 return make_BookStepHelper<IOUAmount, XRPAmount>(ctx,
in,
xrpIssue());
1195 return make_BookStepHelper<XRPAmount, IOUAmount>(ctx,
xrpIssue(),
out);