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;
56 JLOG(j.debug()) <<
"Malformed transaction: Invalid flags set.";
63 if (bImmediateOrCancel && bFillOrKill)
65 JLOG(j.debug()) <<
"Malformed transaction: both IoC and FoK set.";
69 bool const bHaveExpiration(tx.isFieldPresent(
sfExpiration));
71 if (bHaveExpiration && (tx.getFieldU32(
sfExpiration) == 0))
73 JLOG(j.debug()) <<
"Malformed offer: bad expiration";
78 cancelSequence && *cancelSequence == 0)
80 JLOG(j.debug()) <<
"Malformed offer: bad cancel sequence";
92 JLOG(j.debug()) <<
"Malformed offer: redundant (XRP for XRP)";
95 if (saTakerPays <= beast::zero || saTakerGets <= beast::zero)
97 JLOG(j.debug()) <<
"Malformed offer: bad amount";
101 auto const& uPaysIssuerID = saTakerPays.
getIssuer();
102 auto const& uPaysCurrency = saTakerPays.
getCurrency();
104 auto const& uGetsIssuerID = saTakerGets.
getIssuer();
105 auto const& uGetsCurrency = saTakerGets.
getCurrency();
107 if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID)
109 JLOG(j.debug()) <<
"Malformed offer: redundant (IOU for IOU)";
115 JLOG(j.debug()) <<
"Malformed offer: bad currency";
119 if (saTakerPays.
native() != !uPaysIssuerID ||
120 saTakerGets.
native() != !uGetsIssuerID)
122 JLOG(j.debug()) <<
"Malformed offer: bad issuer";
137 auto const& uPaysIssuerID = saTakerPays.getIssuer();
138 auto const& uPaysCurrency = saTakerPays.getCurrency();
140 auto const& uGetsIssuerID = saTakerGets.getIssuer();
155 JLOG(ctx.
j.
debug()) <<
"Offer involves frozen asset";
163 <<
"delay: Offers must be at least partially funded.";
169 if (cancelSequence && (uAccountSequence <= *cancelSequence))
171 JLOG(ctx.
j.
debug()) <<
"uAccountSequenceNext=" << uAccountSequence
172 <<
" uOfferSequence=" << *cancelSequence;
197 if (!saTakerPays.native())
199 auto result = checkAcceptAsset(
204 Issue(uPaysCurrency, uPaysIssuerID));
213 CreateOffer::checkAcceptAsset(
223 auto const issuerAccount = view.
read(keylet::account(issue.
account));
228 <<
"delay: can't receive IOUs from non-existent issuer: "
243 auto const trustLine =
254 bool const canonical_gt(
id > issue.
account);
256 bool const is_authorized(
262 <<
"delay: can't receive IOUs from issuer without auth.";
274 if (offer.fully_consumed())
281 ctx_.app.journal(
"View"));
282 return (amount <= beast::zero);
286 CreateOffer::select_path(
294 assert(have_direct || have_bridge);
300 Quality
const bridged_quality(
307 Quality
const direct_quality(direct.
tip().
quality());
309 if (bridged_quality < direct_quality)
319 CreateOffer::reachedOfferCrossingLimit(
Taker const& taker)
const
321 auto const crossings =
326 return crossings >= 850;
330 CreateOffer::bridged_cross(
338 assert(!
isXRP(takerAmount.in) && !
isXRP(takerAmount.out));
340 if (
isXRP(takerAmount.in) ||
isXRP(takerAmount.out))
341 Throw<std::logic_error>(
"Bridging with XRP and an endpoint.");
372 bool have_bridge = offers_leg1.
step() && offers_leg2.
step();
373 bool have_direct = step_account(offers_direct, taker);
376 auto viewJ = ctx_.app.journal(
"View");
380 while (have_direct || have_bridge)
382 bool leg1_consumed =
false;
383 bool leg2_consumed =
false;
384 bool direct_consumed =
false;
386 auto const [use_direct, quality] = select_path(
387 have_direct, offers_direct, have_bridge, offers_leg1, offers_leg2);
391 if (taker.
reject(quality))
398 if (
auto stream = j_.
debug())
400 stream << count <<
" Direct:";
401 stream <<
" offer: " << offers_direct.
tip();
402 stream <<
" in: " << offers_direct.
tip().
amount().in;
403 stream <<
" out: " << offers_direct.
tip().
amount().out;
404 stream <<
" owner: " << offers_direct.
tip().
owner();
414 cross_result = taker.
cross(offers_direct.
tip());
418 if (dry_offer(view, offers_direct.
tip()))
420 direct_consumed =
true;
421 have_direct = step_account(offers_direct, taker);
426 if (
auto stream = j_.
debug())
442 stream << count <<
" Bridge:";
443 stream <<
" offer1: " << offers_leg1.
tip();
444 stream <<
" in: " << offers_leg1.
tip().
amount().in;
445 stream <<
" out: " << offers_leg1.
tip().
amount().out;
446 stream <<
" owner: " << offers_leg1.
tip().
owner();
447 stream <<
" funds: " << owner1_funds_before;
448 stream <<
" offer2: " << offers_leg2.
tip();
449 stream <<
" in: " << offers_leg2.
tip().
amount().in;
450 stream <<
" out: " << offers_leg2.
tip().
amount().out;
451 stream <<
" owner: " << offers_leg2.
tip().
owner();
452 stream <<
" funds: " << owner2_funds_before;
455 cross_result = taker.
cross(offers_leg1.
tip(), offers_leg2.
tip());
463 leg1_consumed = dry_offer(view, offers_leg1.
tip());
465 have_bridge &= offers_leg1.
step();
467 leg2_consumed = dry_offer(view, offers_leg2.
tip());
469 have_bridge &= offers_leg2.
step();
475 if (dry_offer(view, offers_leg1.
tip()))
477 leg1_consumed =
true;
478 have_bridge = (have_bridge && offers_leg1.
step());
480 if (dry_offer(view, offers_leg2.
tip()))
482 leg2_consumed =
true;
483 have_bridge = (have_bridge && offers_leg2.
step());
496 JLOG(j_.
debug()) <<
"The taker reports he's done during crossing!";
500 if (reachedOfferCrossingLimit(taker))
502 JLOG(j_.
debug()) <<
"The offer crossing limit has been exceeded!";
508 assert(direct_consumed || leg1_consumed || leg2_consumed);
510 if (!direct_consumed && !leg1_consumed && !leg2_consumed)
511 Throw<std::logic_error>(
512 "bridged crossing: nothing was fully consumed.");
519 CreateOffer::direct_cross(
536 bool have_offer = step_account(offers, taker);
542 bool direct_consumed =
false;
543 auto& offer(offers.tip());
546 if (taker.
reject(offer.quality()))
551 if (
auto stream = j_.
debug())
553 stream << count <<
" Direct:";
554 stream <<
" offer: " << offer;
555 stream <<
" in: " << offer.amount().in;
556 stream <<
" out: " << offer.amount().out;
557 stream <<
"quality: " << offer.quality();
558 stream <<
" owner: " << offer.owner();
565 ctx_.app.journal(
"View"));
568 cross_result = taker.
cross(offer);
572 if (dry_offer(view, offer))
574 direct_consumed =
true;
575 have_offer = step_account(offers, taker);
586 JLOG(j_.
debug()) <<
"The taker reports he's done during crossing!";
590 if (reachedOfferCrossingLimit(taker))
592 JLOG(j_.
debug()) <<
"The offer crossing limit has been exceeded!";
598 assert(direct_consumed);
600 if (!direct_consumed)
601 Throw<std::logic_error>(
602 "direct crossing: nothing was fully consumed.");
614 while (stream.step())
616 auto const& offer = stream.tip();
619 if (taker.
reject(offer.quality()))
623 if (offer.owner() != taker.
account())
635 CreateOffer::takerCross(
638 Amounts
const& takerAmount)
661 JLOG(j_.
debug()) <<
"Not crossing: taker is unfunded.";
667 if (cross_type_ == CrossType::IouToIou)
668 return bridged_cross(taker, sb, sbCancel, when);
670 return direct_cross(taker, sb, sbCancel, when);
674 JLOG(j_.
error()) <<
"Exception during offer crossing: " << e.
what();
680 CreateOffer::flowCross(
683 Amounts
const& takerAmount)
695 if (inStartBalance <= beast::zero)
698 JLOG(j_.
debug()) <<
"Not crossing: taker is unfunded.";
705 Rate gatewayXferRate{QUALITY_ONE};
710 if (gatewayXferRate.value != QUALITY_ONE)
715 takerAmount.in.issue(),
722 Quality threshold{takerAmount.out, sendMax};
731 if (sendMax > inStartBalance)
732 sendMax = inStartBalance;
739 if (!takerAmount.in.native() & !takerAmount.out.native())
742 path.emplace_back(std::nullopt,
xrpCurrency(), std::nullopt);
753 deliver =
STAmount{STAmount::cMaxNative};
760 takerAmount.out.
issue(),
761 STAmount::cMaxValue / 2,
762 STAmount::cMaxOffset};
766 auto const result =
flow(
781 for (
auto const& toRemove : result.removableOffers)
783 if (
auto otr = psb.
peek(keylet::offer(toRemove)))
785 if (
auto otr = psbCancel.
peek(keylet::offer(toRemove)))
790 auto afterCross = takerAmount;
796 if (takerInBalance <= beast::zero)
800 afterCross.in.clear();
801 afterCross.out.clear();
806 Quality{takerAmount.out, takerAmount.in}.rate()};
818 STAmount nonGatewayAmountIn = result.actualAmountIn;
819 if (gatewayXferRate.value != QUALITY_ONE)
821 result.actualAmountIn,
823 takerAmount.in.issue(),
826 afterCross.in -= nonGatewayAmountIn;
830 if (afterCross.in < beast::zero)
833 afterCross.in.
clear();
836 afterCross.in, rate, takerAmount.out.issue(),
true);
843 afterCross.out -= result.actualAmountOut;
844 assert(afterCross.out >= beast::zero);
845 if (afterCross.out < beast::zero)
846 afterCross.out.clear();
848 afterCross.out, rate, takerAmount.in.issue(),
true);
858 JLOG(j_.
error()) <<
"Exception during offer crossing: " << e.
what();
870 auto const ret = flowCross(psbFlow, psbCancelFlow, takerAmount);
872 psbCancelFlow.apply(sbCancel);
877 Sandbox sbCancelTaker{&sbCancel};
878 auto const ret = takerCross(sbTaker, sbCancelTaker, takerAmount);
880 sbCancelTaker.apply(sbCancel);
894 CreateOffer::preCompute()
896 cross_type_ = CrossType::IouToIou;
897 bool const pays_xrp = ctx_.tx.getFieldAmount(
sfTakerPays).native();
898 bool const gets_xrp = ctx_.tx.getFieldAmount(
sfTakerGets).native();
899 if (pays_xrp && !gets_xrp)
900 cross_type_ = CrossType::IouToXrp;
901 else if (gets_xrp && !pays_xrp)
902 cross_type_ = CrossType::XrpToIou;
904 return Transactor::preCompute();
914 bool const bPassive(uTxFlags &
tfPassive);
917 bool const bSell(uTxFlags &
tfSell);
926 auto const offerSequence = ctx_.tx.getSeqProxy().value();
931 auto uRate =
getRate(saTakerGets, saTakerPays);
933 auto viewJ = ctx_.app.journal(
"View");
940 auto const sleCancel =
941 sb.
peek(keylet::offer(account_, *cancelSequence));
948 JLOG(j_.
debug()) <<
"Create cancels order " << *cancelSequence;
973 bool const bOpenLedger = sb.open();
974 bool crossed =
false;
976 if (result == tesSUCCESS)
979 auto const& uPaysIssuerID = saTakerPays.getIssuer();
980 auto const& uGetsIssuerID = saTakerGets.getIssuer();
983 if (!
isXRP(uPaysIssuerID))
985 auto const sle = sb.read(keylet::account(uPaysIssuerID));
986 if (sle && sle->isFieldPresent(sfTickSize))
987 uTickSize =
std::min(uTickSize, (*sle)[sfTickSize]);
989 if (!
isXRP(uGetsIssuerID))
991 auto const sle = sb.read(keylet::account(uGetsIssuerID));
992 if (sle && sle->isFieldPresent(sfTickSize))
993 uTickSize =
std::min(uTickSize, (*sle)[sfTickSize]);
995 if (uTickSize < Quality::maxTickSize)
998 Quality{saTakerGets, saTakerPays}.round(uTickSize).rate();
1006 saTakerPays =
multiply(saTakerGets, rate, saTakerPays.issue());
1011 saTakerGets =
divide(saTakerPays, rate, saTakerGets.issue());
1013 if (!saTakerGets || !saTakerPays)
1015 JLOG(j_.
debug()) <<
"Offer rounded to zero";
1016 return {result,
true};
1019 uRate =
getRate(saTakerGets, saTakerPays);
1023 Amounts
const takerAmount(saTakerGets, saTakerPays);
1028 Amounts place_offer;
1030 JLOG(j_.
debug()) <<
"Attempting cross: "
1031 <<
to_string(takerAmount.in.issue()) <<
" -> "
1034 if (
auto stream = j_.
trace())
1036 stream <<
" mode: " << (bPassive ?
"passive " :
"")
1037 << (bSell ?
"sell" :
"buy");
1038 stream <<
" in: " << format_amount(takerAmount.in);
1039 stream <<
" out: " << format_amount(takerAmount.out);
1042 std::tie(result, place_offer) = cross(sb, sbCancel, takerAmount);
1046 assert(result == tesSUCCESS ||
isTecClaim(result));
1048 if (
auto stream = j_.
trace())
1051 stream <<
" in: " << format_amount(place_offer.in);
1052 stream <<
" out: " << format_amount(place_offer.out);
1055 if (result == tecFAILED_PROCESSING && bOpenLedger)
1058 if (result != tesSUCCESS)
1061 return {result,
true};
1064 assert(saTakerGets.issue() == place_offer.in.issue());
1065 assert(saTakerPays.issue() == place_offer.out.issue());
1067 if (takerAmount != place_offer)
1072 if (place_offer.in < zero || place_offer.out < zero)
1074 JLOG(j_.
fatal()) <<
"Cross left offer negative!"
1075 <<
" in: " << format_amount(place_offer.in)
1076 <<
" out: " << format_amount(place_offer.out);
1080 if (place_offer.in == zero || place_offer.out == zero)
1082 JLOG(j_.
debug()) <<
"Offer fully crossed!";
1083 return {result,
true};
1089 saTakerPays = place_offer.out;
1090 saTakerGets = place_offer.in;
1093 assert(saTakerPays > zero && saTakerGets > zero);
1095 if (result != tesSUCCESS)
1098 return {result,
true};
1101 if (
auto stream = j_.
trace())
1103 stream <<
"Place" << (crossed ?
" remaining " :
" ") <<
"offer:";
1104 stream <<
" Pays: " << saTakerPays.getFullText();
1105 stream <<
" Gets: " << saTakerGets.getFullText();
1112 JLOG(j_.
trace()) <<
"Fill or Kill: offer killed";
1113 if (sb.rules().enabled(fix1578))
1120 if (bImmediateOrCancel)
1122 JLOG(j_.
trace()) <<
"Immediate or cancel: offer canceled";
1126 auto const sleCreator = sb.peek(keylet::account(account_));
1132 sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1);
1134 if (mPriorBalance < reserve)
1142 if (result != tesSUCCESS)
1147 return {result,
true};
1152 auto const offer_index = keylet::offer(account_, offerSequence);
1155 auto const ownerNode = sb.dirInsert(
1161 <<
"final result: failed to add offer to owner's directory";
1168 JLOG(j_.
trace()) <<
"adding to book: " <<
to_string(saTakerPays.issue())
1169 <<
" : " <<
to_string(saTakerGets.issue());
1171 Book
const book{saTakerPays.issue(), saTakerGets.issue()};
1175 auto dir = keylet::quality(keylet::book(book), uRate);
1176 bool const bookExisted =
static_cast<bool>(sb.peek(dir));
1178 auto const bookNode = sb.dirAppend(dir, offer_index, [&](SLE::ref sle) {
1179 sle->setFieldH160(sfTakerPaysCurrency, saTakerPays.issue().currency);
1180 sle->setFieldH160(sfTakerPaysIssuer, saTakerPays.issue().account);
1181 sle->setFieldH160(sfTakerGetsCurrency, saTakerGets.issue().currency);
1182 sle->setFieldH160(sfTakerGetsIssuer, saTakerGets.issue().account);
1183 sle->setFieldU64(sfExchangeRate, uRate);
1188 JLOG(j_.
debug()) <<
"final result: failed to add offer to book";
1192 auto sleOffer = std::make_shared<SLE>(offer_index);
1193 sleOffer->setAccountID(sfAccount, account_);
1194 sleOffer->setFieldU32(sfSequence, offerSequence);
1195 sleOffer->setFieldH256(sfBookDirectory, dir.key);
1196 sleOffer->setFieldAmount(sfTakerPays, saTakerPays);
1197 sleOffer->setFieldAmount(sfTakerGets, saTakerGets);
1198 sleOffer->setFieldU64(sfOwnerNode, *ownerNode);
1199 sleOffer->setFieldU64(sfBookNode, *bookNode);
1201 sleOffer->setFieldU32(sfExpiration, *expiration);
1203 sleOffer->setFlag(lsfPassive);
1205 sleOffer->setFlag(lsfSell);
1206 sb.insert(sleOffer);
1209 ctx_.app.getOrderBookDB().addOrderBook(book);
1211 JLOG(j_.
debug()) <<
"final result: success";
1217 CreateOffer::doApply()
1226 Sandbox sbCancel(&ctx_.view());
1228 auto const result = applyGuts(sb, sbCancel);
1230 sb.
apply(ctx_.rawView());
1232 sbCancel.
apply(ctx_.rawView());
1233 return result.first;