1#include <xrpl/basics/Log.h>
2#include <xrpl/ledger/PaymentSandbox.h>
3#include <xrpl/ledger/helpers/AccountRootHelpers.h>
4#include <xrpl/ledger/helpers/RippleStateHelpers.h>
5#include <xrpl/protocol/Feature.h>
6#include <xrpl/protocol/IOUAmount.h>
7#include <xrpl/protocol/Quality.h>
8#include <xrpl/tx/paths/detail/StepChecks.h>
9#include <xrpl/tx/paths/detail/Steps.h>
11#include <boost/container/flat_set.hpp>
18template <
class TDerived>
19class DirectStepI :
public StepImp<IOUAmount, IOUAmount, DirectStepI<TDerived>>
142 boost::container::flat_set<uint256>& ofrsToRm,
149 boost::container::flat_set<uint256>& ofrsToRm,
176 return !(lhs == rhs);
185 <<
"\nSrc: " <<
src_ <<
"\nDst: " <<
dst_;
193 if (
auto ds =
dynamic_cast<DirectStepI const*
>(&rhs))
287 return issues(prevStepDir);
295 return dstQIn == QUALITY_ONE;
333 auto const& field = [&,
this]() ->
SF_UINT32 const& {
337 if (this->dst_ < this->
src_)
339 return sfLowQualityIn;
342 return sfHighQualityIn;
346 if (this->src_ < this->
dst_)
348 return sfLowQualityOut;
351 return sfHighQualityOut;
354 if (!sle->isFieldPresent(field))
357 auto const q = (*sle)[field];
407 JLOG(
j_.
trace()) <<
"DirectStepI: No credit line. " << *
this;
411 auto const authField = (
src_ >
dst_) ? lsfHighAuth : lsfLowAuth;
413 if ((((*sleSrc)[sfFlags] & lsfRequireAuth) != 0u) &&
414 (((*sleLine)[sfFlags] & authField) == 0u) && (*sleLine)[sfBalance] == beast::zero)
416 JLOG(
j_.
debug()) <<
"DirectStepI: can't receive IOUs from issuer without auth."
425 auto const noRippleSrcToDst =
426 ((*sleLine)[sfFlags] & ((
src_ >
dst_) ? lsfHighNoRipple : lsfLowNoRipple));
427 if (noRippleSrcToDst != 0u)
435 if (owed <= beast::zero)
440 JLOG(
j_.
debug()) <<
"DirectStepI: dry: owed: " << owed <<
" limit: " << limit;
459template <
class TDerived>
466 if (srcOwed.signum() > 0)
473template <
class TDerived>
478 return cache_->srcDebtDir;
484template <
class TDerived>
489 boost::container::flat_set<uint256>& ,
494 auto const [maxSrcToDst, srcDebtDir] =
static_cast<TDerived const*
>(
this)->maxFlow(sb,
out);
498 static_cast<TDerived const*
>(
this)->verifyDstQualityIn(dstQIn),
499 "xrpl::DirectStepI : valid destination quality");
501 Issue const srcToDstIss(currency_,
redeems(srcDebtDir) ? dst_ : src_);
503 JLOG(j_.
trace()) <<
"DirectStepI::rev"
505 <<
" maxSrcToDst: " <<
to_string(maxSrcToDst) <<
" srcQOut: " << srcQOut
506 <<
" dstQIn: " << dstQIn;
508 if (maxSrcToDst.signum() <= 0)
510 JLOG(j_.
trace()) <<
"DirectStepI::rev: dry";
513 return {beast::zero, beast::zero};
518 if (srcToDst <= maxSrcToDst)
521 cache_.emplace(
in, srcToDst,
out, srcDebtDir);
529 JLOG(j_.
trace()) <<
"DirectStepI::rev: Non-limiting"
538 cache_.emplace(
in, maxSrcToDst, actualOut, srcDebtDir);
546 JLOG(j_.
trace()) <<
"DirectStepI::rev: Limiting"
549 return {
in, actualOut};
556template <
class TDerived>
564 if (cache_->in < fwdIn)
567 auto const diff = fwdIn - cache_->in;
568 if (diff > smallDiff)
570 if (fwdIn.
exponent() != cache_->in.exponent() || !cache_->in.mantissa() ||
571 (
double(fwdIn.
mantissa()) /
double(cache_->in.mantissa())) > 1.01)
575 JLOG(j_.
warn()) <<
"DirectStepI::fwd: setCacheLimiting"
578 <<
" fwdSrcToDst: " <<
to_string(fwdSrcToDst)
579 <<
" cacheSrcToDst: " <<
to_string(cache_->srcToDst)
581 <<
" cacheOut: " <<
to_string(cache_->out);
582 cache_.emplace(fwdIn, fwdSrcToDst, fwdOut, srcDebtDir);
588 if (fwdSrcToDst < cache_->srcToDst)
589 cache_->srcToDst = fwdSrcToDst;
590 if (fwdOut < cache_->
out)
591 cache_->out = fwdOut;
592 cache_->srcDebtDir = srcDebtDir;
595template <
class TDerived>
600 boost::container::flat_set<uint256>& ,
603 XRPL_ASSERT(cache_,
"xrpl::DirectStepI::fwdImp : cache is set");
605 auto const [maxSrcToDst, srcDebtDir] =
606 static_cast<TDerived const*
>(
this)->maxFlow(sb, cache_->srcToDst);
610 Issue const srcToDstIss(currency_,
redeems(srcDebtDir) ? dst_ : src_);
612 JLOG(j_.
trace()) <<
"DirectStepI::fwd"
614 <<
" maxSrcToDst: " <<
to_string(maxSrcToDst) <<
" srcQOut: " << srcQOut
615 <<
" dstQIn: " << dstQIn;
617 if (maxSrcToDst.signum() <= 0)
619 JLOG(j_.
trace()) <<
"DirectStepI::fwd: dry";
622 return {beast::zero, beast::zero};
627 if (srcToDst <= maxSrcToDst)
630 setCacheLimiting(
in, srcToDst,
out, srcDebtDir);
638 JLOG(j_.
trace()) <<
"DirectStepI::fwd: Non-limiting"
647 setCacheLimiting(actualIn, maxSrcToDst,
out, srcDebtDir);
655 JLOG(j_.
trace()) <<
"DirectStepI::rev: Limiting"
659 return {cache_->in, cache_->out};
662template <
class TDerived>
668 JLOG(j_.
trace()) <<
"Expected valid cache in validFwd";
672 auto const savCache = *cache_;
674 XRPL_ASSERT(!
in.native,
"xrpl::DirectStepI::validFwd : input is not XRP");
676 auto const [maxSrcToDst, srcDebtDir] =
677 static_cast<TDerived const*
>(
this)->maxFlow(sb, cache_->srcToDst);
682 boost::container::flat_set<uint256> dummy;
683 fwdImp(sb, afView, dummy,
in.iou);
685 catch (FlowException
const&)
690 if (maxSrcToDst < cache_->srcToDst)
692 JLOG(j_.
warn()) <<
"DirectStepI: Strand re-execute check failed."
693 <<
" Exceeded max src->dst limit"
694 <<
" max src->dst: " <<
to_string(maxSrcToDst)
695 <<
" actual src->dst: " <<
to_string(cache_->srcToDst);
701 JLOG(j_.
warn()) <<
"DirectStepI: Strand re-execute check failed."
702 <<
" ExpectedIn: " <<
to_string(savCache.in)
703 <<
" CachedIn: " <<
to_string(cache_->in)
704 <<
" ExpectedOut: " <<
to_string(savCache.out)
705 <<
" CachedOut: " <<
to_string(cache_->out);
712template <
class TDerived>
716 if (prevStep_ ==
nullptr)
717 return {QUALITY_ONE, QUALITY_ONE};
719 auto const prevStepQIn = prevStep_->lineQualityIn(sb);
722 if (prevStepQIn > srcQOut)
723 srcQOut = prevStepQIn;
724 return {srcQOut, QUALITY_ONE};
728template <
class TDerived>
736 static_cast<TDerived const*
>(
this)->verifyPrevStepDebtDirection(prevStepDebtDirection),
737 "xrpl::DirectStepI::qualitiesSrcIssues : will prevStepDebtDirection "
744 if (isLast_ && dstQIn > QUALITY_ONE)
745 dstQIn = QUALITY_ONE;
746 return {srcQOut, dstQIn};
750template <
class TDerived>
759 return qualitiesSrcRedeems(sb);
762 auto const prevStepDebtDirection = [&] {
764 return prevStep_->debtDirection(sb, strandDir);
767 return qualitiesSrcIssues(sb, prevStepDebtDirection);
770template <
class TDerived>
778template <
class TDerived>
784 auto const [srcQOut, dstQIn] =
785 redeems(dir) ? qualitiesSrcRedeems(v) : qualitiesSrcIssues(v, prevStepDir);
787 Issue const iss{currency_, src_};
797template <
class TDerived>
804 JLOG(j_.
debug()) <<
"DirectStepI: specified bad account.";
810 JLOG(j_.
debug()) <<
"DirectStepI: same src and dst.";
817 JLOG(j_.
warn()) <<
"DirectStepI: can't receive IOUs from non-existent issuer: " << src_;
841 Issue const srcIssue{currency_, src_};
842 Issue const dstIssue{currency_, dst_};
850 "xrpl::DirectStepI::check : prev seen book without a "
860 if (book->out != srcIssue)
868 JLOG(j_.
debug()) <<
"DirectStepI: loop detected: Index: " << ctx.
strandSize <<
' '
874 return static_cast<TDerived const*
>(
this)->check(ctx, sleSrc);
890 return ds->src() == src && ds->dst() == dst && ds->currency() == currency;
910 ter = offerCrossingStep->check(ctx);
911 r = std::move(offerCrossingStep);
916 ter = paymentStep->check(ctx);
917 r = std::move(paymentStep);
920 return {ter,
nullptr};
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
Writeable view to a ledger, for applying a transaction.
static TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc)
DirectIOfferCrossingStep(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
static bool verifyPrevStepDebtDirection(DebtDirection prevStepDir)
static std::uint32_t quality(ReadView const &sb, QualityDirection qDir)
static bool verifyDstQualityIn(std::uint32_t dstQIn)
std::pair< IOUAmount, DebtDirection > maxFlow(ReadView const &sb, IOUAmount const &desired) const
std::string logString() const override
DirectIPaymentStep(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
std::pair< IOUAmount, DebtDirection > maxFlow(ReadView const &sb, IOUAmount const &desired) const
static bool verifyPrevStepDebtDirection(DebtDirection)
TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc) const
static bool verifyDstQualityIn(std::uint32_t dstQIn)
std::string logString() const override
Step const *const prevStep_
std::pair< IOUAmount, IOUAmount > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, IOUAmount const &out)
Currency const & currency() const
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcIssues(ReadView const &sb, DebtDirection prevStepDebtDirection) const
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcRedeems(ReadView const &sb) const
bool equal(Step const &rhs) const override
DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
std::pair< IOUAmount, IOUAmount > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, IOUAmount const &in)
void setCacheLimiting(IOUAmount const &fwdIn, IOUAmount const &fwdSrcToDst, IOUAmount const &fwdOut, DebtDirection srcDebtDir)
AccountID const & src() const
std::optional< EitherAmount > cachedOut() const override
std::optional< AccountID > directStepSrcAcct() const override
AccountID const & dst() const
friend bool operator==(DirectStepI const &lhs, DirectStepI const &rhs)
std::pair< std::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection dir) const override
std::string logStringImpl(char const *name) const
std::uint32_t lineQualityIn(ReadView const &v) const override
std::optional< Cache > cache_
std::optional< std::pair< AccountID, AccountID > > directStepAccts() const override
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
std::pair< std::uint32_t, std::uint32_t > qualities(ReadView const &sb, DebtDirection srcDebtDir, StrandDirection strandDir) const
std::pair< IOUAmount, DebtDirection > maxPaymentFlow(ReadView const &sb) const
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
TER check(StrandContext const &ctx) const
friend bool operator!=(DirectStepI const &lhs, DirectStepI const &rhs)
std::optional< EitherAmount > cachedIn() const override
Floating point representation of amounts with high dynamic range.
mantissa_type mantissa() const noexcept
exponent_type exponent() const noexcept
A currency issued by an account.
A wrapper which makes credits unavailable to balances.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
A step in a payment path.
virtual std::optional< AccountID > directStepSrcAcct() const
If this step is DirectStepI (IOU->IOU direct step), return the src account.
virtual std::optional< Book > bookStepBook() const
If this step is a BookStep, return the book.
Keylet line(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Keylet account(AccountID const &id) noexcept
AccountID root.
bool directStepEqual(Step const &step, AccountID const &src, AccountID const &dst, Currency const ¤cy)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::string to_string(base_uint< Bits, Tag > const &a)
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
STAmount creditLimit(ReadView const &view, AccountID const &account, AccountID const &issuer, Currency const ¤cy)
Calculate the maximum amount of IOUs that an account can hold.
IOUAmount creditLimit2(ReadView const &v, AccountID const &acc, AccountID const &iss, Currency const &cur)
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.
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const ¤cy)
STAmount creditBalance(ReadView const &view, AccountID const &account, AccountID const &issuer, Currency const ¤cy)
Returns the amount of IOUs issued by issuer that are held by an account.
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
std::pair< TER, std::unique_ptr< Step > > make_DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
IOUAmount toAmount< IOUAmount >(STAmount const &amt)
bool isTesSuccess(TER x) noexcept
TER checkNoRipple(ReadView const &view, AccountID const &prev, AccountID const &cur, AccountID const &next, Currency const ¤cy, beast::Journal j)
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Cache(IOUAmount const &in_, IOUAmount const &srcToDst_, IOUAmount const &out_, DebtDirection srcDebtDir_)
Context needed to build Strand Steps and for error checking.
boost::container::flat_set< Issue > & seenBookOuts
A strand may not include an offer that output the same issue more than once.
size_t const strandSize
Length of Strand.
ReadView const & view
Current ReadView.
bool const isFirst
true if Step is first in Strand
bool const isLast
true if Step is last in Strand
std::array< boost::container::flat_set< Issue >, 2 > & seenDirectIssues
A strand may not include the same account node more than once in the same currency.
Step const *const prevStep
The previous step in the strand.
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
A field with a type known at compile time.