20#include <xrpld/app/paths/Credit.h> 
   21#include <xrpld/app/paths/detail/StepChecks.h> 
   22#include <xrpld/app/paths/detail/Steps.h> 
   24#include <xrpl/basics/Log.h> 
   25#include <xrpl/ledger/PaymentSandbox.h> 
   26#include <xrpl/protocol/Feature.h> 
   27#include <xrpl/protocol/IOUAmount.h> 
   28#include <xrpl/protocol/Quality.h> 
   30#include <boost/container/flat_set.hpp> 
   37template <
class TDerived>
 
   38class DirectStepI : 
public StepImp<IOUAmount, IOUAmount, DirectStepI<TDerived>>
 
  164        boost::container::flat_set<uint256>& ofrsToRm,
 
  171        boost::container::flat_set<uint256>& ofrsToRm,
 
  200        return !(lhs == rhs);
 
 
  209             << 
"\nSrc: " << 
src_ << 
"\nDst: " << 
dst_;
 
 
  217        if (
auto ds = 
dynamic_cast<DirectStepI const*
>(&rhs))
 
 
  294        return issues(prevStepDir);
 
 
  302        return dstQIn == QUALITY_ONE;
 
 
 
  341    auto const& field = [&, 
this]() -> 
SF_UINT32 const& {
 
  345            if (this->dst_ < this->
src_)
 
  346                return sfLowQualityIn;
 
  348                return sfHighQualityIn;
 
  353            if (this->src_ < this->
dst_)
 
  354                return sfLowQualityOut;
 
  356                return sfHighQualityOut;
 
  360    if (!sle->isFieldPresent(field))
 
  363    auto const q = (*sle)[field];
 
 
  416            JLOG(
j_.
trace()) << 
"DirectStepI: No credit line. " << *
this;
 
  423            !((*sleLine)[sfFlags] & authField) &&
 
  424            (*sleLine)[sfBalance] == beast::zero)
 
  427                << 
"DirectStepI: can't receive IOUs from issuer without auth." 
  436                auto const noRippleSrcToDst =
 
  437                    ((*sleLine)[sfFlags] &
 
  439                if (noRippleSrcToDst)
 
  447        if (owed <= beast::zero)
 
  452                JLOG(
j_.
debug()) << 
"DirectStepI: dry: owed: " << owed
 
  453                                 << 
" limit: " << limit;
 
 
  474template <
class TDerived>
 
  481    if (srcOwed.signum() > 0)
 
 
  490template <
class TDerived>
 
  496        return cache_->srcDebtDir;
 
 
  504template <
class TDerived>
 
  509    boost::container::flat_set<uint256>& ,
 
  514    auto const [maxSrcToDst, srcDebtDir] =
 
  515        static_cast<TDerived const*
>(
this)->maxFlow(sb, 
out);
 
  517    auto const [srcQOut, dstQIn] =
 
  520        static_cast<TDerived const*
>(
this)->verifyDstQualityIn(dstQIn),
 
  521        "ripple::DirectStepI : valid destination quality");
 
  523    Issue const srcToDstIss(currency_, 
redeems(srcDebtDir) ? dst_ : src_);
 
  525    JLOG(j_.
trace()) << 
"DirectStepI::rev" 
  526                     << 
" srcRedeems: " << 
redeems(srcDebtDir)
 
  528                     << 
" maxSrcToDst: " << 
to_string(maxSrcToDst)
 
  529                     << 
" srcQOut: " << srcQOut << 
" dstQIn: " << dstQIn;
 
  531    if (maxSrcToDst.signum() <= 0)
 
  533        JLOG(j_.
trace()) << 
"DirectStepI::rev: dry";
 
  539        return {beast::zero, beast::zero};
 
  545    if (srcToDst <= maxSrcToDst)
 
  548            mulRatio(srcToDst, srcQOut, QUALITY_ONE,  
true);
 
  549        cache_.emplace(
in, srcToDst, 
out, srcDebtDir);
 
  557        JLOG(j_.
trace()) << 
"DirectStepI::rev: Non-limiting" 
  558                         << 
" srcRedeems: " << 
redeems(srcDebtDir)
 
  567        mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE,  
true);
 
  569        mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE,  
false);
 
  570    cache_.emplace(
in, maxSrcToDst, actualOut, srcDebtDir);
 
  578    JLOG(j_.
trace()) << 
"DirectStepI::rev: Limiting" 
  579                     << 
" srcRedeems: " << 
redeems(srcDebtDir)
 
  581                     << 
" srcToDst: " << 
to_string(maxSrcToDst)
 
  583    return {
in, actualOut};
 
 
  590template <
class TDerived>
 
  598    if (cache_->in < fwdIn)
 
  601        auto const diff = fwdIn - cache_->in;
 
  602        if (diff > smallDiff)
 
  604            if (fwdIn.
exponent() != cache_->in.exponent() ||
 
  605                !cache_->in.mantissa() ||
 
  606                (
double(fwdIn.
mantissa()) / 
double(cache_->in.mantissa())) >
 
  612                    << 
"DirectStepI::fwd: setCacheLimiting" 
  615                    << 
" fwdSrcToDst: " << 
to_string(fwdSrcToDst)
 
  616                    << 
" cacheSrcToDst: " << 
to_string(cache_->srcToDst)
 
  618                    << 
" cacheOut: " << 
to_string(cache_->out);
 
  619                cache_.emplace(fwdIn, fwdSrcToDst, fwdOut, srcDebtDir);
 
  625    if (fwdSrcToDst < cache_->srcToDst)
 
  626        cache_->srcToDst = fwdSrcToDst;
 
  627    if (fwdOut < cache_->
out)
 
  628        cache_->out = fwdOut;
 
  629    cache_->srcDebtDir = srcDebtDir;
 
 
 
  632template <
class TDerived>
 
  637    boost::container::flat_set<uint256>& ,
 
  640    XRPL_ASSERT(cache_, 
"ripple::DirectStepI::fwdImp : cache is set");
 
  642    auto const [maxSrcToDst, srcDebtDir] =
 
  643        static_cast<TDerived const*
>(
this)->maxFlow(sb, cache_->srcToDst);
 
  645    auto const [srcQOut, dstQIn] =
 
  648    Issue const srcToDstIss(currency_, 
redeems(srcDebtDir) ? dst_ : src_);
 
  650    JLOG(j_.
trace()) << 
"DirectStepI::fwd" 
  651                     << 
" srcRedeems: " << 
redeems(srcDebtDir)
 
  653                     << 
" maxSrcToDst: " << 
to_string(maxSrcToDst)
 
  654                     << 
" srcQOut: " << srcQOut << 
" dstQIn: " << dstQIn;
 
  656    if (maxSrcToDst.signum() <= 0)
 
  658        JLOG(j_.
trace()) << 
"DirectStepI::fwd: dry";
 
  664        return {beast::zero, beast::zero};
 
  670    if (srcToDst <= maxSrcToDst)
 
  673            mulRatio(srcToDst, dstQIn, QUALITY_ONE,  
false);
 
  674        setCacheLimiting(
in, srcToDst, 
out, srcDebtDir);
 
  682        JLOG(j_.
trace()) << 
"DirectStepI::fwd: Non-limiting" 
  683                         << 
" srcRedeems: " << 
redeems(srcDebtDir)
 
  692            mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE,  
true);
 
  694            mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE,  
false);
 
  695        setCacheLimiting(actualIn, maxSrcToDst, 
out, srcDebtDir);
 
  703        JLOG(j_.
trace()) << 
"DirectStepI::rev: Limiting" 
  704                         << 
" srcRedeems: " << 
redeems(srcDebtDir)
 
  709    return {cache_->in, cache_->out};
 
 
  712template <
class TDerived>
 
  721        JLOG(j_.
trace()) << 
"Expected valid cache in validFwd";
 
  725    auto const savCache = *cache_;
 
  727    XRPL_ASSERT(!
in.native, 
"ripple::DirectStepI::validFwd : input is not XRP");
 
  729    auto const [maxSrcToDst, srcDebtDir] =
 
  730        static_cast<TDerived const*
>(
this)->maxFlow(sb, cache_->srcToDst);
 
  735        boost::container::flat_set<uint256> dummy;
 
  736        fwdImp(sb, afView, dummy, 
in.iou);  
 
  738    catch (FlowException 
const&)
 
  743    if (maxSrcToDst < cache_->srcToDst)
 
  745        JLOG(j_.
warn()) << 
"DirectStepI: Strand re-execute check failed." 
  746                        << 
" Exceeded max src->dst limit" 
  747                        << 
" max src->dst: " << 
to_string(maxSrcToDst)
 
  748                        << 
" actual src->dst: " << 
to_string(cache_->srcToDst);
 
  752    if (!(
checkNear(savCache.in, cache_->in) &&
 
  755        JLOG(j_.
warn()) << 
"DirectStepI: Strand re-execute check failed." 
  756                        << 
" ExpectedIn: " << 
to_string(savCache.in)
 
  757                        << 
" CachedIn: " << 
to_string(cache_->in)
 
  758                        << 
" ExpectedOut: " << 
to_string(savCache.out)
 
  759                        << 
" CachedOut: " << 
to_string(cache_->out);
 
 
  766template <
class TDerived>
 
  771        return {QUALITY_ONE, QUALITY_ONE};
 
  773    auto const prevStepQIn = prevStep_->lineQualityIn(sb);
 
  777    if (prevStepQIn > srcQOut)
 
  778        srcQOut = prevStepQIn;
 
  779    return {srcQOut, QUALITY_ONE};
 
 
  783template <
class TDerived>
 
  792        static_cast<TDerived const*
>(
this)->verifyPrevStepDebtDirection(
 
  793            prevStepDebtDirection),
 
  794        "ripple::DirectStepI::qualitiesSrcIssues : will prevStepDebtDirection " 
  803    if (isLast_ && dstQIn > QUALITY_ONE)
 
  804        dstQIn = QUALITY_ONE;
 
  805    return {srcQOut, dstQIn};
 
 
  809template <
class TDerived>
 
  818        return qualitiesSrcRedeems(sb);
 
  822        auto const prevStepDebtDirection = [&] {
 
  824                return prevStep_->debtDirection(sb, strandDir);
 
  827        return qualitiesSrcIssues(sb, prevStepDebtDirection);
 
 
  831template <
class TDerived>
 
  839template <
class TDerived>
 
  847    auto const [srcQOut, dstQIn] = 
redeems(dir)
 
  848        ? qualitiesSrcRedeems(v)
 
  849        : qualitiesSrcIssues(v, prevStepDir);
 
  851    Issue const iss{currency_, src_};
 
 
  862template <
class TDerived>
 
  869        JLOG(j_.
debug()) << 
"DirectStepI: specified bad account.";
 
  875        JLOG(j_.
debug()) << 
"DirectStepI: same src and dst.";
 
  883            << 
"DirectStepI: can't receive IOUs from non-existent issuer: " 
  909        Issue const srcIssue{currency_, src_};
 
  910        Issue const dstIssue{currency_, dst_};
 
  918                    "ripple::DirectStepI::check : prev seen book without a " 
  928                if (book->out != srcIssue)
 
  937                << 
"DirectStepI: loop detected: Index: " << ctx.
strandSize 
  943    return static_cast<TDerived const*
>(
this)->check(ctx, sleSrc);
 
 
 
  959        return ds->src() == src && ds->dst() == dst &&
 
  960            ds->currency() == currency;
 
 
  979        auto offerCrossingStep =
 
  981        ter = offerCrossingStep->check(ctx);
 
  982        r = std::move(offerCrossingStep);
 
  988        ter = paymentStep->check(ctx);
 
  989        r = std::move(paymentStep);
 
  992        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.
 
std::pair< IOUAmount, DebtDirection > maxFlow(ReadView const &sb, IOUAmount const &desired) const
 
std::string logString() const override
 
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
 
bool verifyPrevStepDebtDirection(DebtDirection prevStepDir) const
 
bool verifyDstQualityIn(std::uint32_t dstQIn) const
 
TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc) const
 
bool verifyDstQualityIn(std::uint32_t dstQIn) const
 
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
 
std::string logString() const override
 
std::pair< IOUAmount, DebtDirection > maxFlow(ReadView const &sb, IOUAmount const &desired) const
 
TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc) const
 
bool verifyPrevStepDebtDirection(DebtDirection) const
 
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
 
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcRedeems(ReadView const &sb) const
 
std::pair< IOUAmount, DebtDirection > maxPaymentFlow(ReadView const &sb) const
 
std::optional< EitherAmount > cachedOut() const override
 
std::pair< IOUAmount, IOUAmount > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, IOUAmount const &out)
 
std::string logStringImpl(char const *name) const
 
std::pair< IOUAmount, IOUAmount > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, IOUAmount const &in)
 
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcIssues(ReadView const &sb, DebtDirection prevStepDebtDirection) const
 
void setCacheLimiting(IOUAmount const &fwdIn, IOUAmount const &fwdSrcToDst, IOUAmount const &fwdOut, DebtDirection srcDebtDir)
 
std::uint32_t lineQualityIn(ReadView const &v) const override
 
TER check(StrandContext const &ctx) const
 
Step const  *const prevStep_
 
DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
 
AccountID const & src() const
 
friend bool operator==(DirectStepI const &lhs, DirectStepI const &rhs)
 
std::optional< std::pair< AccountID, AccountID > > directStepAccts() const override
 
AccountID const & dst() const
 
Currency const & currency() const
 
bool equal(Step const &rhs) const override
 
std::optional< EitherAmount > cachedIn() const override
 
std::pair< std::uint32_t, std::uint32_t > qualities(ReadView const &sb, DebtDirection srcDebtDir, StrandDirection strandDir) const
 
std::optional< Cache > cache_
 
friend bool operator!=(DirectStepI const &lhs, DirectStepI const &rhs)
 
std::optional< AccountID > directStepSrcAcct() const override
 
std::pair< std::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection dir) const override
 
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
 
Floating point representation of amounts with high dynamic range.
 
int exponent() const noexcept
 
std::int64_t mantissa() 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< Book > bookStepBook() const
If this step is a BookStep, return the book.
 
virtual std::optional< AccountID > directStepSrcAcct() const
If this step is DirectStepI (IOU->IOU direct step), return the src account.
 
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.
 
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const ¤cy)
 
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
 
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 toAmount< IOUAmount >(STAmount const &amt)
 
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.
 
TER checkNoRipple(ReadView const &view, AccountID const &prev, AccountID const &cur, AccountID const &next, Currency const ¤cy, beast::Journal j)
 
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
 
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
 
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
 
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.
 
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
 
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
 
std::pair< TER, std::unique_ptr< Step > > make_DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
 
std::string to_string(base_uint< Bits, Tag > const &a)
 
IOUAmount creditLimit2(ReadView const &v, AccountID const &acc, AccountID const &iss, Currency const &cur)
 
Cache(IOUAmount const &in_, IOUAmount const &srcToDst_, IOUAmount const &out_, DebtDirection srcDebtDir_)
 
Context needed to build Strand Steps and for error checking.
 
size_t const strandSize
Length of Strand.
 
boost::container::flat_set< Issue > & seenBookOuts
A strand may not include an offer that output the same issue more than once.
 
ReadView const  & view
Current ReadView.
 
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.
 
bool const isFirst
true if Step is first in Strand
 
bool const isLast
true if Step is last in Strand
 
A field with a type known at compile time.