1#include <xrpl/basics/Log.h>
2#include <xrpl/basics/Number.h>
3#include <xrpl/basics/base_uint.h>
4#include <xrpl/beast/utility/Journal.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/beast/utility/instrumentation.h>
7#include <xrpl/core/ServiceRegistry.h>
8#include <xrpl/ledger/PaymentSandbox.h>
9#include <xrpl/ledger/helpers/MPTokenHelpers.h>
10#include <xrpl/ledger/helpers/TokenHelpers.h>
11#include <xrpl/protocol/AccountID.h>
12#include <xrpl/protocol/AmountConversions.h>
13#include <xrpl/protocol/Indexes.h>
14#include <xrpl/protocol/MPTAmount.h>
15#include <xrpl/protocol/MPTIssue.h>
16#include <xrpl/protocol/Protocol.h>
17#include <xrpl/protocol/Quality.h>
18#include <xrpl/protocol/STAmount.h>
19#include <xrpl/protocol/TER.h>
20#include <xrpl/protocol/UintTypes.h>
21#include <xrpl/tx/paths/detail/EitherAmount.h>
22#include <xrpl/tx/paths/detail/Steps.h>
24#include <boost/container/flat_set.hpp>
36template <
class TDerived>
37class MPTEndpointStep :
public StepImp<MPTAmount, MPTAmount, MPTEndpointStep<TDerived>>
113 ctx.strandDst !=
mptIssue_.getIssuer() &&
114 (ctx.isFirst || (ctx.prevStep != nullptr && !ctx.prevStep->bookStepBook())))
119 "MPTEndpointStep::MPTEndpointStep src or dst must be an issuer");
133 [[nodiscard]]
MPTID const&
180 boost::container::flat_set<UInt256>& ofrsToRm,
187 boost::container::flat_set<UInt256>& ofrsToRm,
214 return !(lhs == rhs);
223 <<
"\nSrc: " <<
src_ <<
"\nDst: " <<
dst_;
317 return issues(prevStepDir);
346 auto const& issuer =
mptIssue_.getIssuer();
427 "xrpl::MPTEndpointOfferCrossingStep::checkCreateMPT : create MPToken failed");
428 JLOG(
j_.trace()) <<
"MPTEndpointStep::checkCreateMPT: failed create MPT";
438template <
class TDerived>
462template <
class TDerived>
493template <
class TDerived>
498 return cache_->srcDebtDir;
503template <
class TDerived>
508 boost::container::flat_set<UInt256>& ,
518 JLOG(
j_.trace()) <<
"MPTEndpointStep::rev"
520 <<
" maxSrcToDst: " <<
to_string(maxSrcToDst) <<
" srcQOut: " << srcQOut
521 <<
" dstQIn: " << dstQIn;
523 if (maxSrcToDst.signum() <= 0)
525 JLOG(
j_.trace()) <<
"MPTEndpointStep::rev: dry";
545 cache_.emplace(in, srcToDst, srcToDst, srcDebtDir);
553 UNREACHABLE(
"xrpl::MPTEndpointStep::revImp : send failed");
554 JLOG(
j_.trace()) <<
"MPTEndpointStep::rev: error " << ter;
560 JLOG(
j_.trace()) <<
"MPTEndpointStep::rev: " << (srcToDst < out ?
"Limiting" :
"Non-limiting")
564 return {in, srcToDst};
571template <
class TDerived>
584 auto const diff = fwdIn -
cache_->in;
585 if (diff > smallDiff)
587 if (!
cache_->in.value() ||
592 JLOG(
j_.warn()) <<
"MPTEndpointStep::fwd: setCacheLimiting"
595 <<
" fwdSrcToDst: " <<
to_string(fwdSrcToDst)
599 cache_.emplace(fwdIn, fwdSrcToDst, fwdOut, srcDebtDir);
605 if (fwdSrcToDst < cache_->srcToDst)
606 cache_->srcToDst = fwdSrcToDst;
607 if (fwdOut < cache_->out)
609 cache_->srcDebtDir = srcDebtDir;
613template <
class TDerived>
618 boost::container::flat_set<UInt256>& ,
621 XRPL_ASSERT(
cache_,
"MPTEndpointStep<TDerived>::fwdImp : valid cache");
629 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd"
631 <<
" maxSrcToDst: " <<
to_string(maxSrcToDst) <<
" srcQOut: " << srcQOut
632 <<
" dstQIn: " << dstQIn;
634 if (maxSrcToDst.signum() <= 0)
639 UNREACHABLE(
"xrpl::MPTEndpointStep::fwdImp : dry source");
640 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd: dry";
646 auto const maybeSrcToDst =
tryMulRatio(in, QUALITY_ONE, srcQOut,
false);
652 UNREACHABLE(
"xrpl::MPTEndpointStep::fwdImp : source to destination overflow");
653 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd: overflow";
659 MPTAmount const srcToDst = *maybeSrcToDst;
661 if (srcToDst <= maxSrcToDst)
667 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd: Non-limiting"
676 UNREACHABLE(
"xrpl::MPTEndpointStep::fwdImp : forward pass limiting");
678 auto const maybeActualIn =
tryMulRatio(maxSrcToDst, srcQOut, QUALITY_ONE,
true);
681 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd: overflow";
686 MPTAmount const actualIn = *maybeActualIn;
692 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd: Limiting"
704 UNREACHABLE(
"xrpl::MPTEndpointStep::fwdImp : send failed");
705 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd: error " << ter;
715template <
class TDerived>
721 JLOG(
j_.trace()) <<
"Expected valid cache in validFwd";
725 auto const savCache = *
cache_;
727 XRPL_ASSERT(in.
holds<
MPTAmount>(),
"MPTEndpoint<TDerived>::validFwd : is MPT");
734 boost::container::flat_set<UInt256> dummy;
737 catch (FlowException
const&)
743 if (maxSrcToDst < cache_->srcToDst)
745 JLOG(
j_.warn()) <<
"MPTEndpointStep: Strand re-execute check failed."
746 <<
" Exceeded max src->dst limit"
747 <<
" max src->dst: " <<
to_string(maxSrcToDst)
754 JLOG(
j_.warn()) <<
"MPTEndpointStep: Strand re-execute check failed."
755 <<
" ExpectedIn: " <<
to_string(savCache.in)
757 <<
" ExpectedOut: " <<
to_string(savCache.out)
766template <
class TDerived>
771 return {QUALITY_ONE, QUALITY_ONE};
773 auto const prevStepQIn =
prevStep_->lineQualityIn(sb);
775 auto srcQOut = QUALITY_ONE;
778 return {srcQOut, QUALITY_ONE};
782template <
class TDerived>
791 static_cast<TDerived const*
>(
this)->verifyPrevStepDebtDirection(prevStepDebtDirection),
792 "MPTEndpointStep<TDerived>::qualitiesSrcIssues : verify prev step debt "
799 return {srcQOut, QUALITY_ONE};
803template <
class TDerived>
815 auto const prevStepDebtDirection = [&] {
817 return prevStep_->debtDirection(sb, strandDir);
823template <
class TDerived>
831template <
class TDerived>
837 auto const [srcQOut, dstQIn] =
851template <
class TDerived>
858 JLOG(
j_.debug()) <<
"MPTEndpointStep: specified bad account.";
864 JLOG(
j_.debug()) <<
"MPTEndpointStep: same src and dst.";
871 JLOG(
j_.warn()) <<
"MPTEndpointStep: can't receive MPT from non-existent issuer: " <<
src_;
895 "xrpl::MPTEndpointStep::check : prev seen book without a "
912 JLOG(
j_.debug()) <<
"MPTEndpointStep: loop detected: Index: " << ctx.
strandSize <<
' '
920 JLOG(
j_.warn()) <<
"MPTEndpointStep: MPT can only be an endpoint";
924 auto const& issuer =
mptIssue_.getIssuer();
925 if ((
src_ != issuer &&
dst_ != issuer) || (
src_ == issuer &&
dst_ == issuer))
927 JLOG(
j_.warn()) <<
"MPTEndpointStep: invalid src/dst";
931 return static_cast<TDerived const*
>(
this)->
check(ctx, sleSrc);
934template <
class TDerived>
955 ter = offerCrossingStep->check(ctx);
956 r = std::move(offerCrossingStep);
961 ter = paymentStep->check(ctx);
962 r = std::move(paymentStep);
965 return {ter,
nullptr};
981 return ds->src() == src && ds->dst() == dst && ds->mptID() == mptid;
A generic endpoint for log messages.
Writeable view to a ledger, for applying a transaction.
constexpr value_type value() const
Returns the underlying value.
static bool verifyPrevStepDebtDirection(DebtDirection prevStepDir)
MPTEndpointOfferCrossingStep(StrandContext const &ctx, AccountID const &src, AccountID const &dst, MPTID const &mpt)
TER checkCreateMPT(ApplyView &view)
std::string logString() const override
static TER check(StrandContext const &ctx, SLE::ConstRef sleSrc)
MPTEndpointPaymentStep(StrandContext const &ctx, AccountID const &src, AccountID const &dst, MPTID const &mpt)
static TER checkCreateMPT(ApplyView &)
TER check(StrandContext const &ctx, SLE::ConstRef sleSrc) const
static bool verifyPrevStepDebtDirection(DebtDirection)
std::string logString() const override
std::pair< MPTAmount, MPTAmount > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< UInt256 > &ofrsToRm, MPTAmount const &in)
std::optional< std::pair< AccountID, AccountID > > directStepAccts() const override
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcRedeems(ReadView const &sb) const
bool equal(Step const &rhs) const override
TER sendWithMPTCreate(ApplyView &view, AccountID const &src, AccountID const &dst, MPTAmount const &amount)
AccountID const & dst() const
std::pair< MPTAmount, MPTAmount > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< UInt256 > &ofrsToRm, MPTAmount const &out)
AccountID const & src() const
std::string logStringImpl(char const *name) const
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
MPTEndpointStep(StrandContext const &ctx, AccountID const &src, AccountID const &dst, MPTID const &mpt)
std::pair< std::uint32_t, std::uint32_t > qualities(ReadView const &sb, DebtDirection srcDebtDir, StrandDirection strandDir) const
void setCacheLimiting(MPTAmount const &fwdIn, MPTAmount const &fwdSrcToDst, MPTAmount const &fwdOut, DebtDirection srcDebtDir)
std::uint32_t lineQualityIn(ReadView const &v) const override
std::optional< Cache > cache_
std::optional< EitherAmount > cachedOut() const override
TER check(StrandContext const &ctx) const
std::optional< AccountID > directStepSrcAcct() const override
std::pair< MPTAmount, DebtDirection > maxPaymentFlow(ReadView const &sb) const
MPTID const & mptID() const
friend bool operator==(MPTEndpointStep const &lhs, MPTEndpointStep const &rhs)
void resetCache(DebtDirection dir)
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcIssues(ReadView const &sb, DebtDirection prevStepDebtDirection) const
bool const isDirectBetweenHolders_
std::optional< EitherAmount > cachedIn() const override
friend bool operator!=(MPTEndpointStep const &lhs, MPTEndpointStep const &rhs)
Step const *const prevStep_
std::pair< std::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection dir) const override
Number is a floating point type that can represent a wide range of values.
A wrapper which makes credits unavailable to balances.
Represents the logical ratio of output currency to input currency.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
std::shared_ptr< STLedgerEntry const > const & ConstRef
A step in a payment path.
virtual std::optional< Book > bookStepBook() const
If this step is a BookStep, return the book.
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
bool mptEndpointStepEqual(Step const &step, AccountID const &src, AccountID const &dst, MPTID const &mptid)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::int64_t maxMPTAmount(SLE const &sleIssuance)
TER checkCreateMPT(xrpl::ApplyView &view, xrpl::MPTIssue const &mptIssue, xrpl::AccountID const &holder, SLE::Ref sponsorSle, std::uint32_t flags, beast::Journal j)
bool isIndividualFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue)
Returns true if account's MPToken for mptIssue carries the individual-lock flag (lsfMPTLocked).
MPTAmount toAmount< MPTAmount >(STAmount const &amt)
bool issues(DebtDirection dir)
std::pair< TER, std::unique_ptr< Step > > makeMptEndpointStep(StrandContext const &ctx, AccountID const &src, AccountID const &dst, MPTID const &mpt)
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to, WaiveMPTCanTransfer waive=WaiveMPTCanTransfer::No, std::uint8_t depth=0)
Check whether to may receive the given MPT from from.
std::string to_string(BaseUInt< Bits, Tag > const &a)
TER canTrade(ReadView const &view, Asset const &asset, std::uint8_t depth=0)
Check whether asset may be traded on the DEX.
std::optional< MPTAmount > tryMulRatio(MPTAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Check if the issuer has the global freeze flag set.
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
TER directSendNoFee(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static directSendNoFeeIOU if saAmount represents Issue.
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, std::uint8_t depth=0)
Returns true if account cannot send or receive tokens of mptIssue because a freeze applies.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, std::uint8_t depth=0)
Check if the account lacks required authorization for MPT.
bool redeems(DebtDirection dir)
constexpr std::uint64_t kMaxMpTokenAmount
The maximum amount of MPTokenIssuance.
STAmount toSTAmount(IOUAmount const &iou, Asset const &asset)
Cache(MPTAmount const &in, MPTAmount const &srcToDst, MPTAmount const &out, DebtDirection srcDebtDir)
Context needed to build Strand Steps and for error checking.
Asset const strandDeliver
Asset strand delivers.
size_t const strandSize
Length of Strand.
ReadView const & view
Current ReadView.
bool const isFirst
true if Step is first in Strand
std::array< boost::container::flat_set< Asset >, 2 > & seenDirectAssets
A strand may not include the same account node more than once in the same currency.
bool const isLast
true if Step is last in Strand
boost::container::flat_set< Asset > & seenBookOuts
A strand may not include an offer that output the same issue more than once.
AccountID const strandDst
Strand destination account.
Step const *const prevStep
The previous step in the strand.
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.