20 #include <ripple/app/ledger/OrderBookDB.h>
21 #include <ripple/app/paths/Flow.h>
22 #include <ripple/app/tx/impl/CreateOffer.h>
23 #include <ripple/beast/utility/WrappedSink.h>
24 #include <ripple/ledger/PaymentSandbox.h>
25 #include <ripple/protocol/Feature.h>
26 #include <ripple/protocol/Quality.h>
27 #include <ripple/protocol/st.h>
36 return amount.native() ? amount.xrp() : beast::zero;
55 JLOG(j.debug()) <<
"Malformed transaction: Invalid flags set.";
62 if (bImmediateOrCancel && bFillOrKill)
64 JLOG(j.debug()) <<
"Malformed transaction: both IoC and FoK set.";
68 bool const bHaveExpiration(tx.isFieldPresent(
sfExpiration));
70 if (bHaveExpiration && (tx.getFieldU32(
sfExpiration) == 0))
72 JLOG(j.debug()) <<
"Malformed offer: bad expiration";
77 cancelSequence && *cancelSequence == 0)
79 JLOG(j.debug()) <<
"Malformed offer: bad cancel sequence";
91 JLOG(j.debug()) <<
"Malformed offer: redundant (XRP for XRP)";
94 if (saTakerPays <= beast::zero || saTakerGets <= beast::zero)
96 JLOG(j.debug()) <<
"Malformed offer: bad amount";
100 auto const& uPaysIssuerID = saTakerPays.
getIssuer();
101 auto const& uPaysCurrency = saTakerPays.
getCurrency();
103 auto const& uGetsIssuerID = saTakerGets.
getIssuer();
104 auto const& uGetsCurrency = saTakerGets.
getCurrency();
106 if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID)
108 JLOG(j.debug()) <<
"Malformed offer: redundant (IOU for IOU)";
114 JLOG(j.debug()) <<
"Malformed offer: bad currency";
118 if (saTakerPays.
native() != !uPaysIssuerID ||
119 saTakerGets.
native() != !uGetsIssuerID)
121 JLOG(j.debug()) <<
"Malformed offer: bad issuer";
136 auto const& uPaysIssuerID = saTakerPays.getIssuer();
137 auto const& uPaysCurrency = saTakerPays.getCurrency();
139 auto const& uGetsIssuerID = saTakerGets.getIssuer();
154 JLOG(ctx.
j.
debug()) <<
"Offer involves frozen asset";
162 <<
"delay: Offers must be at least partially funded.";
168 if (cancelSequence && (uAccountSequence <= *cancelSequence))
170 JLOG(ctx.
j.
debug()) <<
"uAccountSequenceNext=" << uAccountSequence
171 <<
" uOfferSequence=" << *cancelSequence;
189 if (!saTakerPays.native())
196 Issue(uPaysCurrency, uPaysIssuerID));
220 <<
"delay: can't receive IOUs from non-existent issuer: "
235 auto const trustLine =
246 bool const canonical_gt(
id > issue.
account);
248 bool const is_authorized(
254 <<
"delay: can't receive IOUs from issuer without auth.";
266 if (offer.fully_consumed())
274 return (amount <= beast::zero);
286 assert(have_direct || have_bridge);
292 Quality
const bridged_quality(
299 Quality
const direct_quality(direct.
tip().
quality());
301 if (bridged_quality < direct_quality)
313 auto const crossings =
318 return crossings >= 850;
330 assert(!
isXRP(takerAmount.in) && !
isXRP(takerAmount.out));
332 if (
isXRP(takerAmount.in) ||
isXRP(takerAmount.out))
333 Throw<std::logic_error>(
"Bridging with XRP and an endpoint.");
364 bool have_bridge = offers_leg1.
step() && offers_leg2.
step();
372 while (have_direct || have_bridge)
374 bool leg1_consumed =
false;
375 bool leg2_consumed =
false;
376 bool direct_consumed =
false;
379 have_direct, offers_direct, have_bridge, offers_leg1, offers_leg2);
383 if (taker.
reject(quality))
392 stream << count <<
" Direct:";
393 stream <<
" offer: " << offers_direct.
tip();
394 stream <<
" in: " << offers_direct.
tip().
amount().in;
395 stream <<
" out: " << offers_direct.
tip().
amount().out;
396 stream <<
" owner: " << offers_direct.
tip().
owner();
406 cross_result = taker.
cross(offers_direct.
tip());
412 direct_consumed =
true;
434 stream << count <<
" Bridge:";
435 stream <<
" offer1: " << offers_leg1.
tip();
436 stream <<
" in: " << offers_leg1.
tip().
amount().in;
437 stream <<
" out: " << offers_leg1.
tip().
amount().out;
438 stream <<
" owner: " << offers_leg1.
tip().
owner();
439 stream <<
" funds: " << owner1_funds_before;
440 stream <<
" offer2: " << offers_leg2.
tip();
441 stream <<
" in: " << offers_leg2.
tip().
amount().in;
442 stream <<
" out: " << offers_leg2.
tip().
amount().out;
443 stream <<
" owner: " << offers_leg2.
tip().
owner();
444 stream <<
" funds: " << owner2_funds_before;
447 cross_result = taker.
cross(offers_leg1.
tip(), offers_leg2.
tip());
457 have_bridge &= offers_leg1.
step();
461 have_bridge &= offers_leg2.
step();
469 leg1_consumed =
true;
470 have_bridge = (have_bridge && offers_leg1.
step());
474 leg2_consumed =
true;
475 have_bridge = (have_bridge && offers_leg2.
step());
488 JLOG(
j_.
debug()) <<
"The taker reports he's done during crossing!";
494 JLOG(
j_.
debug()) <<
"The offer crossing limit has been exceeded!";
500 assert(direct_consumed || leg1_consumed || leg2_consumed);
502 if (!direct_consumed && !leg1_consumed && !leg2_consumed)
503 Throw<std::logic_error>(
504 "bridged crossing: nothing was fully consumed.");
534 bool direct_consumed =
false;
535 auto& offer(offers.tip());
538 if (taker.
reject(offer.quality()))
545 stream << count <<
" Direct:";
546 stream <<
" offer: " << offer;
547 stream <<
" in: " << offer.amount().in;
548 stream <<
" out: " << offer.amount().out;
549 stream <<
"quality: " << offer.quality();
550 stream <<
" owner: " << offer.owner();
560 cross_result = taker.
cross(offer);
566 direct_consumed =
true;
578 JLOG(
j_.
debug()) <<
"The taker reports he's done during crossing!";
584 JLOG(
j_.
debug()) <<
"The offer crossing limit has been exceeded!";
590 assert(direct_consumed);
592 if (!direct_consumed)
593 Throw<std::logic_error>(
594 "direct crossing: nothing was fully consumed.");
606 while (stream.step())
608 auto const& offer = stream.tip();
611 if (taker.
reject(offer.quality()))
615 if (offer.owner() != taker.
account())
630 Amounts
const& takerAmount)
653 JLOG(
j_.
debug()) <<
"Not crossing: taker is unfunded.";
666 JLOG(
j_.
error()) <<
"Exception during offer crossing: " << e.
what();
675 Amounts
const& takerAmount)
687 if (inStartBalance <= beast::zero)
690 JLOG(
j_.
debug()) <<
"Not crossing: taker is unfunded.";
697 Rate gatewayXferRate{QUALITY_ONE};
702 if (gatewayXferRate.value != QUALITY_ONE)
707 takerAmount.in.issue(),
714 Quality threshold{takerAmount.out, sendMax};
723 if (sendMax > inStartBalance)
724 sendMax = inStartBalance;
731 if (!takerAmount.in.native() && !takerAmount.out.native())
734 path.emplace_back(std::nullopt,
xrpCurrency(), std::nullopt);
752 takerAmount.out.
issue(),
758 auto const result =
flow(
773 for (
auto const& toRemove : result.removableOffers)
782 auto afterCross = takerAmount;
788 if (takerInBalance <= beast::zero)
792 afterCross.in.clear();
793 afterCross.out.clear();
798 Quality{takerAmount.out, takerAmount.in}.rate()};
810 STAmount nonGatewayAmountIn = result.actualAmountIn;
811 if (gatewayXferRate.value != QUALITY_ONE)
813 result.actualAmountIn,
815 takerAmount.in.issue(),
818 afterCross.in -= nonGatewayAmountIn;
822 if (afterCross.in < beast::zero)
825 afterCross.in.
clear();
828 afterCross.in, rate, takerAmount.out.issue(),
true);
835 afterCross.out -= result.actualAmountOut;
836 assert(afterCross.out >= beast::zero);
837 if (afterCross.out < beast::zero)
838 afterCross.out.clear();
840 afterCross.out, rate, takerAmount.in.issue(),
true);
850 JLOG(
j_.
error()) <<
"Exception during offer crossing: " << e.
what();
862 auto const ret =
flowCross(psbFlow, psbCancelFlow, takerAmount);
864 psbCancelFlow.apply(sbCancel);
869 Sandbox sbCancelTaker{&sbCancel};
870 auto const ret =
takerCross(sbTaker, sbCancelTaker, takerAmount);
872 sbCancelTaker.apply(sbCancel);
891 if (pays_xrp && !gets_xrp)
893 else if (gets_xrp && !pays_xrp)
906 bool const bPassive(uTxFlags &
tfPassive);
909 bool const bSell(uTxFlags &
tfSell);
923 auto uRate =
getRate(saTakerGets, saTakerPays);
932 auto const sleCancel =
940 JLOG(
j_.
debug()) <<
"Create cancels order " << *cancelSequence;
960 bool const bOpenLedger = sb.
open();
961 bool crossed =
false;
966 auto const& uPaysIssuerID = saTakerPays.getIssuer();
967 auto const& uGetsIssuerID = saTakerGets.getIssuer();
970 if (!
isXRP(uPaysIssuerID))
976 if (!
isXRP(uGetsIssuerID))
982 if (uTickSize < Quality::maxTickSize)
985 Quality{saTakerGets, saTakerPays}.round(uTickSize).rate();
993 saTakerPays =
multiply(saTakerGets, rate, saTakerPays.issue());
998 saTakerGets =
divide(saTakerPays, rate, saTakerGets.issue());
1000 if (!saTakerGets || !saTakerPays)
1002 JLOG(
j_.
debug()) <<
"Offer rounded to zero";
1003 return {result,
true};
1006 uRate =
getRate(saTakerGets, saTakerPays);
1010 Amounts
const takerAmount(saTakerGets, saTakerPays);
1015 Amounts place_offer;
1017 JLOG(
j_.
debug()) <<
"Attempting cross: "
1018 <<
to_string(takerAmount.in.issue()) <<
" -> "
1023 stream <<
" mode: " << (bPassive ?
"passive " :
"")
1024 << (bSell ?
"sell" :
"buy");
1029 std::tie(result, place_offer) =
cross(sb, sbCancel, takerAmount);
1037 stream <<
"Cross result: " <<
transToken(result);
1048 return {result,
true};
1051 assert(saTakerGets.issue() == place_offer.in.issue());
1052 assert(saTakerPays.issue() == place_offer.out.issue());
1054 if (takerAmount != place_offer)
1059 if (place_offer.in < zero || place_offer.out < zero)
1061 JLOG(
j_.
fatal()) <<
"Cross left offer negative!"
1067 if (place_offer.in == zero || place_offer.out == zero)
1069 JLOG(
j_.
debug()) <<
"Offer fully crossed!";
1070 return {result,
true};
1076 saTakerPays = place_offer.out;
1077 saTakerGets = place_offer.in;
1080 assert(saTakerPays > zero && saTakerGets > zero);
1085 return {result,
true};
1090 stream <<
"Place" << (crossed ?
" remaining " :
" ") <<
"offer:";
1091 stream <<
" Pays: " << saTakerPays.getFullText();
1092 stream <<
" Gets: " << saTakerGets.getFullText();
1099 JLOG(
j_.
trace()) <<
"Fill or Kill: offer killed";
1107 if (bImmediateOrCancel)
1109 JLOG(
j_.
trace()) <<
"Immediate or cancel: offer canceled";
1140 return {result,
true};
1154 <<
"final result: failed to add offer to owner's directory";
1162 <<
" : " <<
to_string(saTakerGets.issue());
1164 Book const book{saTakerPays.issue(), saTakerGets.issue()};
1169 bool const bookExisted =
static_cast<bool>(sb.
peek(dir));
1181 JLOG(
j_.
debug()) <<
"final result: failed to add offer to book";
1185 auto sleOffer = std::make_shared<SLE>(offer_index);
1187 sleOffer->setFieldU32(
sfSequence, offerSequence);
1189 sleOffer->setFieldAmount(
sfTakerPays, saTakerPays);
1190 sleOffer->setFieldAmount(
sfTakerGets, saTakerGets);
1192 sleOffer->setFieldU64(
sfBookNode, *bookNode);
1204 JLOG(
j_.
debug()) <<
"final result: success";
1221 auto const result =
applyGuts(sb, sbCancel);
1226 return result.first;
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
const SF_UINT32 sfOfferSequence
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
const SF_UINT32 sfOwnerCount
Rate transferRate(ReadView const &view, AccountID const &issuer)
virtual OrderBookDB & getOrderBookDB()=0
std::pair< TER, Amounts > takerCross(Sandbox &sb, Sandbox &sbCancel, Amounts const &takerAmount)
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
std::pair< TER, Amounts > flowCross(PaymentSandbox &psb, PaymentSandbox &psbCancel, Amounts const &takerAmount)
A currency issued by an account.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
const SF_UINT64 sfOwnerNode
Represents a transfer rate.
Stream trace() const
Severity stream access functions.
Quality composed_quality(Quality const &lhs, Quality const &rhs)
Issue const & issue() const
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
A wrapper which makes credits unavailable to balances.
bool unfunded() const
Returns true if the taker has run out of funds.
const uint256 featureDepositPreauth
const SF_UINT32 sfSequence
const SF_UINT256 sfBookDirectory
const SF_UINT160 sfTakerPaysCurrency
bool reject(Quality const &quality) const noexcept
Returns true if the quality does not meet the taker's requirements.
Amounts remaining_offer() const
Returns the amount remaining on the offer.
std::string getText() const override
static const std::uint64_t cMaxNative
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
constexpr std::uint32_t tfOfferCreateMask
STAmount multiplyRound(STAmount const &amount, Rate const &rate, bool roundUp)
std::string transToken(TER code)
void emplace_back(Args &&... args)
Issue const & issue_in() const
Returns the Issue associated with the input of the offer.
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
SeqProxy getSeqProxy() const
STAmount divideRound(STAmount const &amount, Rate const &rate, bool roundUp)
bool isLegalNet(STAmount const &value)
constexpr std::uint32_t tfPassive
Presents and consumes the offers in an order book.
std::uint32_t get_bridge_crossings() const
std::uint32_t get_direct_crossings() const
static NotTEC preflight(PreflightContext const &ctx)
Enforce constraints beyond those of the Transactor base class.
Fees const & fees() const override
Returns the fees for the base ledger.
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
const SF_UINT160 sfTakerGetsCurrency
std::pair< TER, bool > applyGuts(Sandbox &view, Sandbox &view_cancel)
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Writeable view to a ledger, for applying a transaction.
STAmount mulRound(STAmount const &v1, STAmount const &v2, Issue const &issue, bool roundUp)
const Quality quality() const noexcept
Returns the quality of the offer.
AccountID const & getIssuer() const
const SF_UINT32 sfExpiration
TAmounts< TIn, TOut > const & amount() const
Returns the in and out amounts.
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
STAmount divide(STAmount const &amount, Rate const &rate)
const SF_UINT160 sfTakerGetsIssuer
const SF_AMOUNT sfTakerPays
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
static std::string format_amount(STAmount const &amount)
Issue const & issue_out() const
Returns the Issue associated with the output of the offer.
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
path::RippleCalc::Output flow(PaymentSandbox &view, STAmount const &deliver, AccountID const &src, AccountID const &dst, STPathSet const &paths, bool defaultPaths, bool partialPayment, bool ownerPaysTransferFee, bool offerCrossing, std::optional< Quality > const &limitQuality, std::optional< STAmount > const &sendMax, beast::Journal j, path::detail::FlowDebugInfo *flowDebugInfo=nullptr)
Make a payment from the src account to the dst account.
std::pair< TER, Amounts > bridged_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
bool open() const override
Returns true if this reflects an open ledger.
static TER preclaim(PreclaimContext const &ctx)
Enforce constraints beyond those of the Transactor base class.
std::optional< std::uint64_t > dirAppend(Keylet const &directory, Keylet const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Append an entry to a directory.
Keylet account(AccountID const &id) noexcept
AccountID root.
void preCompute() override
Gather information beyond what the Transactor base class gathers.
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
STAmount divRound(STAmount const &num, STAmount const &den, Issue const &issue, bool roundUp)
AccountID const & owner() const
Returns the account id of the offer's owner.
static bool step_account(OfferStream &stream, Taker const &taker)
const SF_UINT64 sfBookNode
Discardable, editable view to a ledger.
const uint256 fixTakerDryOfferRemoval
static TxConsequences makeTxConsequences(PreflightContext const &ctx)
Amounts const & original_offer() const
Returns the amount that the offer was originally placed at.
std::uint32_t getFlags() const
const SF_AMOUNT sfTakerGets
const SF_UINT8 sfTickSize
bool isXRP(AccountID const &c)
A generic endpoint for log messages.
TER doApply() override
Precondition: fee collection is likely.
const SF_UINT64 sfExchangeRate
Keylet line(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
constexpr std::uint32_t tfFillOrKill
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
STAmount multiply(STAmount const &amount, Rate const &rate)
State information when determining if a tx is likely to claim a fee.
void insert(std::shared_ptr< SLE > const &sle) override
Insert a new state SLE.
constexpr std::uint32_t value() const
bool native() const noexcept
std::pair< TER, Amounts > direct_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
bool done() const
Returns true if order crossing should not continue.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::uint32_t tfSell
virtual beast::Journal journal(std::string const &name)=0
const uint256 featureImmediateOfferKilled
std::pair< TER, Amounts > cross(Sandbox &sb, Sandbox &sbCancel, Amounts const &takerAmount)
bool reachedOfferCrossingLimit(Taker const &taker) const
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
bool dry_offer(ApplyView &view, Offer const &offer)
TOffer< TIn, TOut > & tip() const
Returns the offer at the tip of the order book.
const SF_UINT160 sfTakerPaysIssuer
virtual Rules const & rules() const =0
Returns the tx processing rules.
static TER checkAcceptAsset(ReadView const &view, ApplyFlags const flags, AccountID const id, beast::Journal const j, Issue const &issue)
Wraps a Journal::Sink to prefix its output with a string.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
const SF_ACCOUNT sfAccount
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Rules const & rules() const override
Returns the tx processing rules.
Keylet quality(Keylet const &k, std::uint64_t q) noexcept
The initial directory page for a specific quality.
State information when preflighting a tx.
bool step()
Advance to the next valid offer.
static const int cMaxOffset
static std::pair< bool, Quality > select_path(bool have_direct, OfferStream const &direct, bool have_bridge, OfferStream const &leg1, OfferStream const &leg2)
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
AccountID const & account() const noexcept
Returns the account identifier of the taker.
const uint256 featureFlowCross
Class describing the consequences to the account of applying a transaction if the transaction consume...
Currency const & getCurrency() const
OfferStream::StepCounter stepCounter_
constexpr std::uint32_t tfImmediateOrCancel
virtual void preCompute()
STAmount const & getFieldAmount(SField const &field) const
Currency const & xrpCurrency()
XRP currency.
TER cross(Offer &offer)
Perform a direct or bridged offer crossing as appropriate.
static const std::uint64_t cMaxValue
void addOrderBook(Book const &)