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/Quality.h>
17#include <xrpl/protocol/STAmount.h>
18#include <xrpl/protocol/TER.h>
19#include <xrpl/protocol/UintTypes.h>
20#include <xrpl/tx/paths/detail/EitherAmount.h>
21#include <xrpl/tx/paths/detail/Steps.h>
23#include <boost/container/flat_set.hpp>
35template <
class TDerived>
36class MPTEndpointStep :
public StepImp<MPTAmount, MPTAmount, MPTEndpointStep<TDerived>>
105 ctx.strandDst !=
mptIssue_.getIssuer() &&
106 (ctx.isFirst || (ctx.prevStep != nullptr && !ctx.prevStep->bookStepBook())))
111 "MPTEndpointStep::MPTEndpointStep src or dst must be an issuer");
125 [[nodiscard]]
MPTID const&
172 boost::container::flat_set<uint256>& ofrsToRm,
179 boost::container::flat_set<uint256>& ofrsToRm,
206 return !(lhs == rhs);
215 <<
"\nSrc: " <<
src_ <<
"\nDst: " <<
dst_;
309 return issues(prevStepDir);
338 auto const& issuer =
mptIssue_.getIssuer();
387 if (owed <= beast::kZero)
415 JLOG(
j_.trace()) <<
"MPTEndpointStep::checkCreateMPT: failed create MPT";
425template <
class TDerived>
456template <
class TDerived>
461 return cache_->srcDebtDir;
466template <
class TDerived>
471 boost::container::flat_set<uint256>& ,
483 JLOG(
j_.trace()) <<
"MPTEndpointStep::rev"
485 <<
" maxSrcToDst: " <<
to_string(maxSrcToDst) <<
" srcQOut: " << srcQOut
486 <<
" dstQIn: " << dstQIn;
488 if (maxSrcToDst.signum() <= 0)
490 JLOG(
j_.trace()) <<
"MPTEndpointStep::rev: dry";
492 return {beast::kZero, beast::kZero};
497 return {beast::kZero, beast::kZero};
502 if (srcToDst <= maxSrcToDst)
505 cache_.emplace(in, srcToDst, srcToDst, srcDebtDir);
515 JLOG(
j_.trace()) <<
"MPTEndpointStep::rev: error " << ter;
517 return {beast::kZero, beast::kZero};
519 JLOG(
j_.trace()) <<
"MPTEndpointStep::rev: Non-limiting"
529 cache_.emplace(in, maxSrcToDst, actualOut, srcDebtDir);
540 JLOG(
j_.trace()) <<
"MPTEndpointStep::rev: error " << ter;
542 return {beast::kZero, beast::kZero};
544 JLOG(
j_.trace()) <<
"MPTEndpointStep::rev: Limiting"
547 return {in, actualOut};
554template <
class TDerived>
567 auto const diff = fwdIn -
cache_->in;
568 if (diff > smallDiff)
570 if (!
cache_->in.value() ||
575 JLOG(
j_.warn()) <<
"MPTEndpointStep::fwd: setCacheLimiting"
578 <<
" fwdSrcToDst: " <<
to_string(fwdSrcToDst)
582 cache_.emplace(fwdIn, fwdSrcToDst, fwdOut, srcDebtDir);
588 if (fwdSrcToDst < cache_->srcToDst)
589 cache_->srcToDst = fwdSrcToDst;
590 if (fwdOut < cache_->out)
592 cache_->srcDebtDir = srcDebtDir;
596template <
class TDerived>
601 boost::container::flat_set<uint256>& ,
604 XRPL_ASSERT(
cache_,
"MPTEndpointStep<TDerived>::fwdImp : valid cache");
614 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd"
616 <<
" maxSrcToDst: " <<
to_string(maxSrcToDst) <<
" srcQOut: " << srcQOut
617 <<
" dstQIn: " << dstQIn;
619 if (maxSrcToDst.signum() <= 0)
621 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd: dry";
623 return {beast::kZero, beast::kZero};
628 return {beast::kZero, beast::kZero};
632 if (srcToDst <= maxSrcToDst)
646 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd: error " << ter;
648 return {beast::kZero, beast::kZero};
650 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd: Non-limiting"
670 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd: error " << ter;
672 return {beast::kZero, beast::kZero};
674 JLOG(
j_.trace()) <<
"MPTEndpointStep::fwd: Limiting"
682template <
class TDerived>
688 JLOG(
j_.trace()) <<
"Expected valid cache in validFwd";
692 auto const savCache = *
cache_;
694 XRPL_ASSERT(in.
holds<
MPTAmount>(),
"MPTEndpoint<TDerived>::validFwd : is MPT");
701 boost::container::flat_set<uint256> dummy;
704 catch (FlowException
const&)
710 if (maxSrcToDst < cache_->srcToDst)
712 JLOG(
j_.warn()) <<
"MPTEndpointStep: Strand re-execute check failed."
713 <<
" Exceeded max src->dst limit"
714 <<
" max src->dst: " <<
to_string(maxSrcToDst)
721 JLOG(
j_.warn()) <<
"MPTEndpointStep: Strand re-execute check failed."
722 <<
" ExpectedIn: " <<
to_string(savCache.in)
724 <<
" ExpectedOut: " <<
to_string(savCache.out)
733template <
class TDerived>
738 return {QUALITY_ONE, QUALITY_ONE};
740 auto const prevStepQIn =
prevStep_->lineQualityIn(sb);
742 auto srcQOut = QUALITY_ONE;
745 return {srcQOut, QUALITY_ONE};
749template <
class TDerived>
758 static_cast<TDerived const*
>(
this)->verifyPrevStepDebtDirection(prevStepDebtDirection),
759 "MPTEndpointStep<TDerived>::qualitiesSrcIssues : verify prev step debt "
766 return {srcQOut, QUALITY_ONE};
770template <
class TDerived>
782 auto const prevStepDebtDirection = [&] {
784 return prevStep_->debtDirection(sb, strandDir);
790template <
class TDerived>
798template <
class TDerived>
804 auto const [srcQOut, dstQIn] =
818template <
class TDerived>
825 JLOG(
j_.debug()) <<
"MPTEndpointStep: specified bad account.";
831 JLOG(
j_.debug()) <<
"MPTEndpointStep: same src and dst.";
838 JLOG(
j_.warn()) <<
"MPTEndpointStep: can't receive MPT from non-existent issuer: " <<
src_;
862 "xrpl::MPTEndpointStep::check : prev seen book without a "
879 JLOG(
j_.debug()) <<
"MPTEndpointStep: loop detected: Index: " << ctx.
strandSize <<
' '
887 JLOG(
j_.warn()) <<
"MPTEndpointStep: MPT can only be an endpoint";
891 auto const& issuer =
mptIssue_.getIssuer();
892 if ((
src_ != issuer &&
dst_ != issuer) || (
src_ == issuer &&
dst_ == issuer))
894 JLOG(
j_.warn()) <<
"MPTEndpointStep: invalid src/dst";
898 return static_cast<TDerived const*
>(
this)->
check(ctx, sleSrc);
901template <
class TDerived>
922 ter = offerCrossingStep->check(ctx);
923 r = std::move(offerCrossingStep);
928 ter = paymentStep->check(ctx);
929 r = std::move(paymentStep);
932 return {ter,
nullptr};
948 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)
static TER check(StrandContext const &ctx, SLE::const_ref sleSrc)
TER checkCreateMPT(ApplyView &view, DebtDirection srcDebtDir)
std::string logString() const override
MPTEndpointPaymentStep(StrandContext const &ctx, AccountID const &src, AccountID const &dst, MPTID const &mpt)
static TER checkCreateMPT(ApplyView &, DebtDirection)
static bool verifyPrevStepDebtDirection(DebtDirection)
TER check(StrandContext const &ctx, SLE::const_ref sleSrc) const
std::string logString() const override
std::pair< MPTAmount, MPTAmount > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, MPTAmount const &out)
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
AccountID const & dst() const
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
std::pair< MPTAmount, MPTAmount > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, MPTAmount const &in)
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 & const_ref
A step in a payment path.
virtual std::optional< Book > bookStepBook() const
If this step is a BookStep, return the book.
Keylet mptokenIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
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)
bool isIndividualFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue)
TER checkCreateMPT(xrpl::ApplyView &view, xrpl::MPTIssue const &mptIssue, xrpl::AccountID const &holder, beast::Journal j)
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.
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)
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)
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.