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,
536 if (limit < stpAmt.out)
540 stpAmt.out, transferRateOut, QUALITY_ONE,
false);
541 ofrAmt = ofrQ.ceil_out(ofrAmt, stpAmt.out);
543 mulRatio(ofrAmt.in, transferRateIn, QUALITY_ONE,
true);
547 template <
class TIn,
class TOut,
class TDerived>
548 template <
class Callback>
554 Callback& callback)
const
562 if (
isXRP(
id) ||
id == this->strandDst_)
568 redeems(prevStepDir) ? rate(book_.in.account) : QUALITY_ONE;
571 ownerPaysTransferFee_ ? rate(book_.out.account) : QUALITY_ONE;
574 maxOffersToConsume_, j_);
580 bool offerAttempted =
false;
582 while (offers.step())
584 auto& offer = offers.tip();
589 ofrQ = offer.quality();
590 else if (*ofrQ != offer.quality())
593 if (
static_cast<TDerived const*
>(
this)->limitSelfCrossQuality(
594 strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
599 if (flowCross && (!
isXRP(offer.issueIn().currency)) &&
600 (offer.owner() != offer.issueIn().account))
602 auto const& issuerID = offer.issueIn().account;
607 auto const& ownerID = offer.owner();
608 auto const authFlag =
611 auto const line = afView.
read(
612 keylet::line(ownerID, issuerID, offer.issueIn().currency));
614 if (!line || (((*line)[
sfFlags] & authFlag) == 0))
618 offers.permRmOffer(offer.
key());
628 if (!
static_cast<TDerived const*
>(
this)->checkQualityThreshold(offer))
631 auto const ofrInRate =
static_cast<TDerived const*
>(
this)->getOfrInRate(
632 prevStep_, offer, trIn);
634 auto const ofrOutRate =
635 static_cast<TDerived const*
>(
this)->getOfrOutRate(
636 prevStep_, offer, strandDst_, trOut);
638 auto ofrAmt = offer.amount();
640 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true),
645 mulRatio(ofrAmt.out, ofrOutRate, QUALITY_ONE,
false);
647 auto const funds = (offer.owner() == offer.issueOut().account)
649 : offers.ownerFunds();
651 if (funds < ownerGives)
656 ownerGives, QUALITY_ONE, ofrOutRate,
false);
657 ofrAmt = ofrQ->ceil_out(ofrAmt, stpAmt.out);
659 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE,
true);
662 offerAttempted =
true;
663 if (!callback(offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate))
667 return {offers.permToRemove(), counter.
count()};
670 template <
class TIn,
class TOut,
class TDerived>
675 TAmounts<TIn, TOut>
const& ofrAmt,
676 TAmounts<TIn, TOut>
const& stepAmt,
677 TOut
const& ownerGives)
const
689 Throw<FlowException>(dr);
702 Throw<FlowException>(cr);
705 offer.consume(sb, ofrAmt);
708 template <
class TCollection>
710 sum(TCollection
const& col)
714 return TResult{beast::zero};
718 template <
class TIn,
class TOut,
class TDerived>
723 boost::container::flat_set<uint256>& ofrsToRm,
728 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
730 auto remainingOut =
out;
732 boost::container::flat_multiset<TIn> savedIns;
733 savedIns.reserve(64);
734 boost::container::flat_multiset<TOut> savedOuts;
735 savedOuts.reserve(64);
742 TAmounts<TIn, TOut>
const& ofrAmt,
743 TAmounts<TIn, TOut>
const& stpAmt,
744 TOut
const& ownerGives,
747 if (remainingOut <= beast::zero)
750 if (stpAmt.out <= remainingOut)
752 savedIns.insert(stpAmt.in);
753 savedOuts.insert(stpAmt.out);
754 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
755 remainingOut =
out - result.out;
756 this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
763 auto ofrAdjAmt = ofrAmt;
764 auto stpAdjAmt = stpAmt;
765 auto ownerGivesAdj = ownerGives;
774 remainingOut = beast::zero;
775 savedIns.insert(stpAdjAmt.in);
776 savedOuts.insert(remainingOut);
777 result.in =
sum(savedIns);
779 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
786 return offer.fully_consumed();
791 auto const prevStepDebtDir = [&] {
796 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
797 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
799 offersUsed_ = offersConsumed;
802 if (offersConsumed >= maxOffersToConsume_)
808 cache_.emplace(beast::zero, beast::zero);
809 return {beast::zero, beast::zero};
818 switch (remainingOut.signum())
823 <<
"BookStep remainingOut < 0 " <<
to_string(remainingOut);
825 cache_.emplace(beast::zero, beast::zero);
826 return {beast::zero, beast::zero};
835 cache_.emplace(result.in, result.out);
836 return {result.in, result.out};
839 template <
class TIn,
class TOut,
class TDerived>
844 boost::container::flat_set<uint256>& ofrsToRm,
849 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
851 auto remainingIn =
in;
853 boost::container::flat_multiset<TIn> savedIns;
854 savedIns.reserve(64);
855 boost::container::flat_multiset<TOut> savedOuts;
856 savedOuts.reserve(64);
861 TAmounts<TIn, TOut>
const& ofrAmt,
862 TAmounts<TIn, TOut>
const& stpAmt,
863 TOut
const& ownerGives,
868 if (remainingIn <= beast::zero)
871 bool processMore =
true;
872 auto ofrAdjAmt = ofrAmt;
873 auto stpAdjAmt = stpAmt;
874 auto ownerGivesAdj = ownerGives;
876 typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
877 if (stpAmt.in <= remainingIn)
879 savedIns.insert(stpAmt.in);
880 lastOut = savedOuts.insert(stpAmt.out);
881 result = TAmounts<TIn, TOut>(
sum(savedIns),
sum(savedOuts));
895 savedIns.insert(remainingIn);
896 lastOut = savedOuts.insert(stpAdjAmt.out);
897 result.out =
sum(savedOuts);
903 if (result.out > cache_->out && result.in <= cache_->in)
912 auto const lastOutAmt = *lastOut;
913 savedOuts.erase(lastOut);
914 auto const remainingOut = cache_->out -
sum(savedOuts);
915 auto ofrAdjAmtRev = ofrAmt;
916 auto stpAdjAmtRev = stpAmt;
917 auto ownerGivesAdjRev = ownerGives;
927 if (stpAdjAmtRev.in == remainingIn)
930 result.out = cache_->out;
933 savedIns.insert(result.in);
935 savedOuts.insert(result.out);
937 ofrAdjAmt = ofrAdjAmtRev;
938 stpAdjAmt.in = remainingIn;
939 stpAdjAmt.out = remainingOut;
940 ownerGivesAdj = ownerGivesAdjRev;
946 savedOuts.insert(lastOutAmt);
950 remainingIn =
in - result.in;
951 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
957 return processMore || offer.fully_consumed();
961 auto const prevStepDebtDir = [&] {
966 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
967 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
969 offersUsed_ = offersConsumed;
972 if (offersConsumed >= maxOffersToConsume_)
978 cache_.emplace(beast::zero, beast::zero);
979 return {beast::zero, beast::zero};
988 switch (remainingIn.signum())
993 <<
"BookStep remainingIn < 0 " <<
to_string(remainingIn);
995 cache_.emplace(beast::zero, beast::zero);
996 return {beast::zero, beast::zero};
1005 cache_.emplace(result.in, result.out);
1006 return {result.in, result.out};
1009 template <
class TIn,
class TOut,
class TDerived>
1018 JLOG(j_.
trace()) <<
"Expected valid cache in validFwd";
1022 auto const savCache = *cache_;
1026 boost::container::flat_set<uint256> dummy;
1027 fwdImp(sb, afView, dummy, get<TIn>(
in));
1029 catch (FlowException
const&)
1034 if (!(
checkNear(savCache.in, cache_->in) &&
1037 JLOG(j_.
warn()) <<
"Strand re-execute check failed."
1038 <<
" ExpectedIn: " <<
to_string(savCache.in)
1039 <<
" CachedIn: " <<
to_string(cache_->in)
1040 <<
" ExpectedOut: " <<
to_string(savCache.out)
1041 <<
" CachedOut: " <<
to_string(cache_->out);
1047 template <
class TIn,
class TOut,
class TDerived>
1051 if (book_.in == book_.out)
1053 JLOG(j_.
debug()) <<
"BookStep: Book with same in and out issuer "
1059 JLOG(j_.
debug()) <<
"Book: currency is inconsistent with issuer."
1069 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1075 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1079 auto issuerExists = [](
ReadView const& view,
Issue const& iss) ->
bool {
1083 if (!issuerExists(ctx.
view, book_.in) || !issuerExists(ctx.
view, book_.out))
1085 JLOG(j_.
debug()) <<
"BookStep: deleted issuer detected: " << *
this;
1093 auto const& view = ctx.
view;
1094 auto const& cur = book_.in.account;
1113 template <
class TIn,
class TOut,
class TDerived>
1118 return book == bs->book();
1127 if (inXRP && outXRP)
1132 if (inXRP && !outXRP)
1137 if (!inXRP && outXRP)
1142 if (!inXRP && !outXRP)
1153 template <
class TIn,
class TOut>
1161 auto offerCrossingStep =
1162 std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx,
in,
out);
1163 ter = offerCrossingStep->check(ctx);
1164 r = std::move(offerCrossingStep);
1169 std::make_unique<BookPaymentStep<TIn, TOut>>(ctx,
in,
out);
1170 ter = paymentStep->check(ctx);
1171 r = std::move(paymentStep);
1174 return {ter,
nullptr};
1182 return make_BookStepHelper<IOUAmount, IOUAmount>(ctx,
in,
out);
1188 return make_BookStepHelper<IOUAmount, XRPAmount>(ctx,
in,
xrpIssue());
1194 return make_BookStepHelper<XRPAmount, IOUAmount>(ctx,
xrpIssue(),
out);