20 #include <ripple/app/paths/impl/FlatSets.h>
21 #include <ripple/app/paths/impl/Steps.h>
22 #include <ripple/app/paths/Credit.h>
23 #include <ripple/app/paths/NodeDirectory.h>
24 #include <ripple/app/tx/impl/OfferStream.h>
25 #include <ripple/basics/contract.h>
26 #include <ripple/basics/IOUAmount.h>
27 #include <ripple/basics/Log.h>
28 #include <ripple/basics/XRPAmount.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_)
63 :
in (in_),
out (out_)
97 boost::optional<EitherAmount>
105 boost::optional<EitherAmount>
120 boost::optional<Book>
133 boost::container::flat_set<uint256>& ofrsToRm,
140 boost::container::flat_set<uint256>& ofrsToRm,
177 return ! (lhs == rhs);
180 bool equal (
Step const& rhs)
const override;
187 template <
class Callback>
193 Callback& callback)
const;
197 TAmounts<TIn, TOut>
const& ofrAmt,
198 TAmounts<TIn, TOut>
const& stepAmt,
199 TOut
const& ownerGives)
const;
211 template<
class TIn,
class TOut>
213 :
public BookStep<TIn, TOut, BookPaymentStep<TIn, TOut>>
284 template<
class TIn,
class TOut>
286 :
public BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>
292 static Quality
getQuality (boost::optional<Quality>
const& limitQuality)
295 assert (limitQuality);
297 Throw<FlowException> (
tefINTERNAL,
"Offer requires quality.");
298 return *limitQuality;
313 bool const offerAttempted)
const
345 strandSrc == offer.owner() && strandDst == offer.owner())
348 offers.permRmOffer (offer.key());
373 auto const srcAcct = prevStep ?
379 offer.owner() == *srcAcct
380 ? QUALITY_ONE : trIn;
390 offer.owner() == strandDst
391 ? QUALITY_ONE : trOut;
420 template <
class TIn,
class TOut,
class TDerived>
424 return book_ == bs->book_;
428 template <
class TIn,
class TOut,
class TDerived>
440 return {boost::none, dir};
442 Quality
const q =
static_cast<TDerived const*
>(
this)->adjustQualityWithFees(
448 template <
class TIn,
class TOut>
451 TAmounts<TIn, TOut>& ofrAmt,
452 TAmounts<TIn, TOut>& stpAmt,
458 if (limit < stpAmt.in)
462 stpAmt.in, QUALITY_ONE, transferRateIn,
false);
463 ofrAmt = ofrQ.ceil_in (ofrAmt, inLmt);
464 stpAmt.out = ofrAmt.out;
466 ofrAmt.out, transferRateOut, QUALITY_ONE,
false);
471 template <
class TIn,
class TOut>
474 TAmounts<TIn, TOut>& ofrAmt,
475 TAmounts<TIn, TOut>& stpAmt,
481 if (limit < stpAmt.out)
485 stpAmt.out, transferRateOut, QUALITY_ONE,
false);
486 ofrAmt = ofrQ.ceil_out (ofrAmt, stpAmt.out);
488 ofrAmt.in, transferRateIn, QUALITY_ONE,
true);
492 template <
class TIn,
class TOut,
class TDerived>
493 template <
class Callback>
499 Callback& callback)
const
507 if (
isXRP (
id) ||
id == this->strandDst_)
513 redeems(prevStepDir) ? rate(book_.in.account) : QUALITY_ONE;
516 ? rate (book_.out.account)
520 counter (maxOffersToConsume_, j_);
526 bool offerAttempted =
false;
527 boost::optional<Quality> ofrQ;
528 while (offers.step ())
530 auto& offer = offers.tip ();
535 ofrQ = offer.quality ();
536 else if (*ofrQ != offer.quality ())
539 if (
static_cast<TDerived const*
>(
this)->limitSelfCrossQuality (
540 strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
546 (!
isXRP (offer.issueIn().currency)) &&
547 (offer.owner() != offer.issueIn().account))
549 auto const& issuerID = offer.issueIn().account;
554 auto const& ownerID = offer.owner();
555 auto const authFlag =
559 ownerID, issuerID, offer.issueIn().currency));
561 if (!line || (((*line)[
sfFlags] & authFlag) == 0))
565 offers.permRmOffer (offer.key());
575 if (!
static_cast<TDerived const*
>(
this)->checkQualityThreshold(offer))
578 auto const ofrInRate =
579 static_cast<TDerived const*
>(
this)->getOfrInRate (
580 prevStep_, offer, trIn);
582 auto const ofrOutRate =
583 static_cast<TDerived const*
>(
this)->getOfrOutRate (
584 prevStep_, offer, strandDst_, trOut);
586 auto ofrAmt = offer.amount ();
588 mulRatio (ofrAmt.in, ofrInRate, QUALITY_ONE,
true),
593 mulRatio (ofrAmt.out, ofrOutRate, QUALITY_ONE,
false);
596 (offer.owner () == offer.issueOut ().account)
598 : offers.ownerFunds ();
600 if (funds < ownerGives)
605 ownerGives, QUALITY_ONE, ofrOutRate,
false);
606 ofrAmt = ofrQ->ceil_out (ofrAmt, stpAmt.out);
608 ofrAmt.in, ofrInRate, QUALITY_ONE,
true);
611 offerAttempted =
true;
613 offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate))
617 return {offers.permToRemove (), counter.
count()};
620 template <
class TIn,
class TOut,
class TDerived>
624 TAmounts<TIn, TOut>
const& ofrAmt,
625 TAmounts<TIn, TOut>
const& stepAmt,
626 TOut
const& ownerGives)
const
631 auto const dr =
accountSend (sb, book_.in.account, offer.owner (),
634 Throw<FlowException> (dr);
640 auto const cr =
accountSend (sb, offer.owner (), book_.out.account,
643 Throw<FlowException> (cr);
646 offer.consume (sb, ofrAmt);
649 template<
class TCollection>
651 auto sum (TCollection
const& col)
655 return TResult{beast::zero};
659 template<
class TIn,
class TOut,
class TDerived>
664 boost::container::flat_set<uint256>& ofrsToRm,
669 TAmounts<TIn, TOut> result (beast::zero, beast::zero);
671 auto remainingOut =
out;
673 boost::container::flat_multiset<TIn> savedIns;
674 savedIns.reserve(64);
675 boost::container::flat_multiset<TOut> savedOuts;
676 savedOuts.reserve(64);
684 TAmounts<TIn, TOut>
const& ofrAmt,
685 TAmounts<TIn, TOut>
const& stpAmt,
686 TOut
const& ownerGives,
690 if (remainingOut <= beast::zero)
693 if (stpAmt.out <= remainingOut)
695 savedIns.insert(stpAmt.in);
696 savedOuts.insert(stpAmt.out);
697 result = TAmounts<TIn, TOut>(
sum (savedIns),
sum(savedOuts));
698 remainingOut =
out - result.out;
699 this->consumeOffer (sb, offer, ofrAmt, stpAmt, ownerGives);
706 auto ofrAdjAmt = ofrAmt;
707 auto stpAdjAmt = stpAmt;
708 auto ownerGivesAdj = ownerGives;
709 limitStepOut (offer.quality (), ofrAdjAmt, stpAdjAmt, ownerGivesAdj,
710 transferRateIn, transferRateOut, remainingOut);
711 remainingOut = beast::zero;
712 savedIns.insert (stpAdjAmt.in);
713 savedOuts.insert (remainingOut);
714 result.in =
sum(savedIns);
716 this->consumeOffer (sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
723 return offer.fully_consumed();
728 auto const prevStepDebtDir = [&]{
733 auto const r = forEachOffer (sb, afView, prevStepDebtDir, eachOffer);
734 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
738 if (offersConsumed >= maxOffersToConsume_)
744 cache_.emplace(beast::zero, beast::zero);
745 return {beast::zero, beast::zero};
754 switch(remainingOut.signum())
760 <<
"BookStep remainingOut < 0 " <<
to_string (remainingOut);
762 cache_.emplace (beast::zero, beast::zero);
763 return {beast::zero, beast::zero};
773 cache_.emplace (result.in, result.out);
774 return {result.in, result.out};
777 template<
class TIn,
class TOut,
class TDerived>
782 boost::container::flat_set<uint256>& ofrsToRm,
787 TAmounts<TIn, TOut> result (beast::zero, beast::zero);
789 auto remainingIn =
in;
791 boost::container::flat_multiset<TIn> savedIns;
792 savedIns.reserve(64);
793 boost::container::flat_multiset<TOut> savedOuts;
794 savedOuts.reserve(64);
800 TAmounts<TIn, TOut>
const& ofrAmt,
801 TAmounts<TIn, TOut>
const& stpAmt,
802 TOut
const& ownerGives,
808 if (remainingIn <= beast::zero)
811 bool processMore =
true;
812 auto ofrAdjAmt = ofrAmt;
813 auto stpAdjAmt = stpAmt;
814 auto ownerGivesAdj = ownerGives;
816 typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
817 if (stpAmt.in <= remainingIn)
819 savedIns.insert(stpAmt.in);
820 lastOut = savedOuts.insert(stpAmt.out);
821 result = TAmounts<TIn, TOut>(
sum (savedIns),
sum(savedOuts));
827 limitStepIn (offer.quality (), ofrAdjAmt, stpAdjAmt, ownerGivesAdj,
828 transferRateIn, transferRateOut, remainingIn);
829 savedIns.insert (remainingIn);
830 lastOut = savedOuts.insert (stpAdjAmt.out);
831 result.out =
sum (savedOuts);
837 if (result.out > cache_->out && result.in <= cache_->in)
846 auto const lastOutAmt = *lastOut;
847 savedOuts.erase(lastOut);
848 auto const remainingOut = cache_->out -
sum (savedOuts);
849 auto ofrAdjAmtRev = ofrAmt;
850 auto stpAdjAmtRev = stpAmt;
851 auto ownerGivesAdjRev = ownerGives;
852 limitStepOut (offer.quality (), ofrAdjAmtRev, stpAdjAmtRev,
853 ownerGivesAdjRev, transferRateIn, transferRateOut,
856 if (stpAdjAmtRev.in == remainingIn)
859 result.out = cache_->out;
862 savedIns.insert(result.in);
864 savedOuts.insert(result.out);
866 ofrAdjAmt = ofrAdjAmtRev;
867 stpAdjAmt.in = remainingIn;
868 stpAdjAmt.out = remainingOut;
869 ownerGivesAdj = ownerGivesAdjRev;
875 savedOuts.insert (lastOutAmt);
879 remainingIn =
in - result.in;
880 this->consumeOffer (sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
886 return processMore || offer.fully_consumed();
890 auto const prevStepDebtDir = [&] {
895 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
896 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
900 if (offersConsumed >= maxOffersToConsume_)
906 cache_.emplace(beast::zero, beast::zero);
907 return {beast::zero, beast::zero};
916 switch(remainingIn.signum())
922 <<
"BookStep remainingIn < 0 " <<
to_string (remainingIn);
924 cache_.emplace (beast::zero, beast::zero);
925 return {beast::zero, beast::zero};
935 cache_.emplace (result.in, result.out);
936 return {result.in, result.out};
939 template<
class TIn,
class TOut,
class TDerived>
948 JLOG (j_.
trace()) <<
"Expected valid cache in validFwd";
952 auto const savCache = *cache_;
956 boost::container::flat_set<uint256> dummy;
957 fwdImp (sb, afView, dummy, get<TIn> (
in));
959 catch (FlowException
const&)
964 if (!(
checkNear (savCache.in, cache_->in) &&
968 "Strand re-execute check failed." <<
969 " ExpectedIn: " <<
to_string (savCache.in) <<
970 " CachedIn: " <<
to_string (cache_->in) <<
971 " ExpectedOut: " <<
to_string (savCache.out) <<
972 " CachedOut: " <<
to_string (cache_->out);
978 template<
class TIn,
class TOut,
class TDerived>
982 if (book_.in == book_.out)
984 JLOG (j_.
debug()) <<
"BookStep: Book with same in and out issuer " << *
this;
989 JLOG (j_.
debug()) <<
"Book: currency is inconsistent with issuer." << *
this;
998 JLOG (j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1004 JLOG(j_.
debug()) <<
"BookStep: loop detected: " << *
this;
1008 auto issuerExists = [](
ReadView const& view,
Issue const& iss) ->
bool {
1012 if (!issuerExists(ctx.
view, book_.in) || !issuerExists(ctx.
view, book_.out))
1014 JLOG(j_.
debug()) <<
"BookStep: deleted issuer detected: " << *
this;
1022 auto const& view = ctx.
view;
1023 auto const& cur = book_.in.account;
1044 template <
class TIn,
class TOut,
class TDerived>
1049 return book == bs->book ();
1057 if (inXRP && outXRP)
1060 if (inXRP && !outXRP)
1063 if (!inXRP && outXRP)
1066 if (!inXRP && !outXRP)
1075 template <
class TIn,
class TOut>
1087 auto offerCrossingStep =
1088 std::make_unique<BookOfferCrossingStep<TIn, TOut>> (ctx,
in,
out);
1089 ter = offerCrossingStep->check (ctx);
1090 r = std::move (offerCrossingStep);
1095 std::make_unique<BookPaymentStep<TIn, TOut>> (ctx,
in,
out);
1096 ter = paymentStep->check (ctx);
1097 r = std::move (paymentStep);
1100 return {ter,
nullptr};
1111 return make_BookStepHelper<IOUAmount, IOUAmount> (ctx,
in,
out);
1119 return make_BookStepHelper<IOUAmount, XRPAmount> (ctx,
in,
xrpIssue());
1127 return make_BookStepHelper<XRPAmount, IOUAmount> (ctx,
xrpIssue(),
out);