rippled
Loading...
Searching...
No Matches
CreateOffer.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/ledger/OrderBookDB.h>
21#include <xrpld/app/paths/Flow.h>
22#include <xrpld/app/tx/detail/CreateOffer.h>
23#include <xrpld/ledger/PaymentSandbox.h>
24#include <xrpl/beast/utility/WrappedSink.h>
25#include <xrpl/protocol/Feature.h>
26#include <xrpl/protocol/Quality.h>
27#include <xrpl/protocol/st.h>
28
29namespace ripple {
30
31TxConsequences
33{
34 auto calculateMaxXRPSpend = [](STTx const& tx) -> XRPAmount {
35 auto const& amount{tx[sfTakerGets]};
36 return amount.native() ? amount.xrp() : beast::zero;
37 };
38
39 return TxConsequences{ctx.tx, calculateMaxXRPSpend(ctx.tx)};
40}
41
44{
45 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
46 return ret;
47
48 auto& tx = ctx.tx;
49 auto& j = ctx.j;
50
51 std::uint32_t const uTxFlags = tx.getFlags();
52
53 if (uTxFlags & tfOfferCreateMask)
54 {
55 JLOG(j.debug()) << "Malformed transaction: Invalid flags set.";
56 return temINVALID_FLAG;
57 }
58
59 bool const bImmediateOrCancel(uTxFlags & tfImmediateOrCancel);
60 bool const bFillOrKill(uTxFlags & tfFillOrKill);
61
62 if (bImmediateOrCancel && bFillOrKill)
63 {
64 JLOG(j.debug()) << "Malformed transaction: both IoC and FoK set.";
65 return temINVALID_FLAG;
66 }
67
68 bool const bHaveExpiration(tx.isFieldPresent(sfExpiration));
69
70 if (bHaveExpiration && (tx.getFieldU32(sfExpiration) == 0))
71 {
72 JLOG(j.debug()) << "Malformed offer: bad expiration";
73 return temBAD_EXPIRATION;
74 }
75
76 if (auto const cancelSequence = tx[~sfOfferSequence];
77 cancelSequence && *cancelSequence == 0)
78 {
79 JLOG(j.debug()) << "Malformed offer: bad cancel sequence";
80 return temBAD_SEQUENCE;
81 }
82
83 STAmount saTakerPays = tx[sfTakerPays];
84 STAmount saTakerGets = tx[sfTakerGets];
85
86 if (!isLegalNet(saTakerPays) || !isLegalNet(saTakerGets))
87 return temBAD_AMOUNT;
88
89 if (saTakerPays.native() && saTakerGets.native())
90 {
91 JLOG(j.debug()) << "Malformed offer: redundant (XRP for XRP)";
92 return temBAD_OFFER;
93 }
94 if (saTakerPays <= beast::zero || saTakerGets <= beast::zero)
95 {
96 JLOG(j.debug()) << "Malformed offer: bad amount";
97 return temBAD_OFFER;
98 }
99
100 auto const& uPaysIssuerID = saTakerPays.getIssuer();
101 auto const& uPaysCurrency = saTakerPays.getCurrency();
102
103 auto const& uGetsIssuerID = saTakerGets.getIssuer();
104 auto const& uGetsCurrency = saTakerGets.getCurrency();
105
106 if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID)
107 {
108 JLOG(j.debug()) << "Malformed offer: redundant (IOU for IOU)";
109 return temREDUNDANT;
110 }
111 // We don't allow a non-native currency to use the currency code XRP.
112 if (badCurrency() == uPaysCurrency || badCurrency() == uGetsCurrency)
113 {
114 JLOG(j.debug()) << "Malformed offer: bad currency";
115 return temBAD_CURRENCY;
116 }
117
118 if (saTakerPays.native() != !uPaysIssuerID ||
119 saTakerGets.native() != !uGetsIssuerID)
120 {
121 JLOG(j.debug()) << "Malformed offer: bad issuer";
122 return temBAD_ISSUER;
123 }
124
125 return preflight2(ctx);
126}
127
128TER
130{
131 auto const id = ctx.tx[sfAccount];
132
133 auto saTakerPays = ctx.tx[sfTakerPays];
134 auto saTakerGets = ctx.tx[sfTakerGets];
135
136 auto const& uPaysIssuerID = saTakerPays.getIssuer();
137 auto const& uPaysCurrency = saTakerPays.getCurrency();
138
139 auto const& uGetsIssuerID = saTakerGets.getIssuer();
140
141 auto const cancelSequence = ctx.tx[~sfOfferSequence];
142
143 auto const sleCreator = ctx.view.read(keylet::account(id));
144 if (!sleCreator)
145 return terNO_ACCOUNT;
146
147 std::uint32_t const uAccountSequence = sleCreator->getFieldU32(sfSequence);
148
149 auto viewJ = ctx.app.journal("View");
150
151 if (isGlobalFrozen(ctx.view, uPaysIssuerID) ||
152 isGlobalFrozen(ctx.view, uGetsIssuerID))
153 {
154 JLOG(ctx.j.debug()) << "Offer involves frozen asset";
155 return tecFROZEN;
156 }
157
158 if (accountFunds(ctx.view, id, saTakerGets, fhZERO_IF_FROZEN, viewJ) <=
159 beast::zero)
160 {
161 JLOG(ctx.j.debug())
162 << "delay: Offers must be at least partially funded.";
163 return tecUNFUNDED_OFFER;
164 }
165
166 // This can probably be simplified to make sure that you cancel sequences
167 // before the transaction sequence number.
168 if (cancelSequence && (uAccountSequence <= *cancelSequence))
169 {
170 JLOG(ctx.j.debug()) << "uAccountSequenceNext=" << uAccountSequence
171 << " uOfferSequence=" << *cancelSequence;
172 return temBAD_SEQUENCE;
173 }
174
175 if (hasExpired(ctx.view, ctx.tx[~sfExpiration]))
176 {
177 // Note that this will get checked again in applyGuts, but it saves
178 // us a call to checkAcceptAsset and possible false negative.
179 //
180 // The return code change is attached to featureDepositPreauth as a
181 // convenience, as the change is not big enough to deserve its own
182 // amendment.
183 return ctx.view.rules().enabled(featureDepositPreauth)
184 ? TER{tecEXPIRED}
185 : TER{tesSUCCESS};
186 }
187
188 // Make sure that we are authorized to hold what the taker will pay us.
189 if (!saTakerPays.native())
190 {
191 auto result = checkAcceptAsset(
192 ctx.view,
193 ctx.flags,
194 id,
195 ctx.j,
196 Issue(uPaysCurrency, uPaysIssuerID));
197 if (result != tesSUCCESS)
198 return result;
199 }
200
201 return tesSUCCESS;
202}
203
204TER
206 ReadView const& view,
207 ApplyFlags const flags,
208 AccountID const id,
209 beast::Journal const j,
210 Issue const& issue)
211{
212 // Only valid for custom currencies
213 XRPL_ASSERT(
214 !isXRP(issue.currency),
215 "ripple::CreateOffer::checkAcceptAsset : input is not XRP");
216
217 auto const issuerAccount = view.read(keylet::account(issue.account));
218
219 if (!issuerAccount)
220 {
221 JLOG(j.debug())
222 << "delay: can't receive IOUs from non-existent issuer: "
223 << to_string(issue.account);
224
225 return (flags & tapRETRY) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER};
226 }
227
228 // This code is attached to the DepositPreauth amendment as a matter of
229 // convenience. The change is not significant enough to deserve its
230 // own amendment.
231 if (view.rules().enabled(featureDepositPreauth) && (issue.account == id))
232 // An account can always accept its own issuance.
233 return tesSUCCESS;
234
235 if ((*issuerAccount)[sfFlags] & lsfRequireAuth)
236 {
237 auto const trustLine =
238 view.read(keylet::line(id, issue.account, issue.currency));
239
240 if (!trustLine)
241 {
242 return (flags & tapRETRY) ? TER{terNO_LINE} : TER{tecNO_LINE};
243 }
244
245 // Entries have a canonical representation, determined by a
246 // lexicographical "greater than" comparison employing strict weak
247 // ordering. Determine which entry we need to access.
248 bool const canonical_gt(id > issue.account);
249
250 bool const is_authorized(
251 (*trustLine)[sfFlags] & (canonical_gt ? lsfLowAuth : lsfHighAuth));
252
253 if (!is_authorized)
254 {
255 JLOG(j.debug())
256 << "delay: can't receive IOUs from issuer without auth.";
257
258 return (flags & tapRETRY) ? TER{terNO_AUTH} : TER{tecNO_AUTH};
259 }
260 }
261
262 return tesSUCCESS;
263}
264
265bool
267{
268 if (offer.fully_consumed())
269 return true;
270 auto const amount = accountFunds(
271 view,
272 offer.owner(),
273 offer.amount().out,
275 ctx_.app.journal("View"));
276 return (amount <= beast::zero);
277}
278
281 bool have_direct,
282 OfferStream const& direct,
283 bool have_bridge,
284 OfferStream const& leg1,
285 OfferStream const& leg2)
286{
287 // If we don't have any viable path, why are we here?!
288 XRPL_ASSERT(
289 have_direct || have_bridge,
290 "ripple::CreateOffer::select_path : valid inputs");
291
292 // If there's no bridged path, the direct is the best by default.
293 if (!have_bridge)
294 return std::make_pair(true, direct.tip().quality());
295
296 Quality const bridged_quality(
297 composed_quality(leg1.tip().quality(), leg2.tip().quality()));
298
299 if (have_direct)
300 {
301 // We compare the quality of the composed quality of the bridged
302 // offers and compare it against the direct offer to pick the best.
303 Quality const direct_quality(direct.tip().quality());
304
305 if (bridged_quality < direct_quality)
306 return std::make_pair(true, direct_quality);
307 }
308
309 // Either there was no direct offer, or it didn't have a better quality
310 // than the bridge.
311 return std::make_pair(false, bridged_quality);
312}
313
314bool
316{
317 auto const crossings =
318 taker.get_direct_crossings() + (2 * taker.get_bridge_crossings());
319
320 // The crossing limit is part of the Ripple protocol and
321 // changing it is a transaction-processing change.
322 return crossings >= 850;
323}
324
327 Taker& taker,
328 ApplyView& view,
329 ApplyView& view_cancel,
330 NetClock::time_point const when)
331{
332 auto const& takerAmount = taker.original_offer();
333
334 XRPL_ASSERT(
335 !isXRP(takerAmount.in) && !isXRP(takerAmount.out),
336 "ripple::CreateOffer::bridged_cross : neither is XRP");
337
338 if (isXRP(takerAmount.in) || isXRP(takerAmount.out))
339 Throw<std::logic_error>("Bridging with XRP and an endpoint.");
340
341 OfferStream offers_direct(
342 view,
343 view_cancel,
344 Book(taker.issue_in(), taker.issue_out()),
345 when,
347 j_);
348
349 OfferStream offers_leg1(
350 view,
351 view_cancel,
352 Book(taker.issue_in(), xrpIssue()),
353 when,
355 j_);
356
357 OfferStream offers_leg2(
358 view,
359 view_cancel,
360 Book(xrpIssue(), taker.issue_out()),
361 when,
363 j_);
364
365 TER cross_result = tesSUCCESS;
366
367 // Note the subtle distinction here: self-offers encountered in the
368 // bridge are taken, but self-offers encountered in the direct book
369 // are not.
370 bool have_bridge = offers_leg1.step() && offers_leg2.step();
371 bool have_direct = step_account(offers_direct, taker);
372 int count = 0;
373
374 auto viewJ = ctx_.app.journal("View");
375
376 // Modifying the order or logic of the operations in the loop will cause
377 // a protocol breaking change.
378 while (have_direct || have_bridge)
379 {
380 bool leg1_consumed = false;
381 bool leg2_consumed = false;
382 bool direct_consumed = false;
383
384 auto const [use_direct, quality] = select_path(
385 have_direct, offers_direct, have_bridge, offers_leg1, offers_leg2);
386
387 // We are always looking at the best quality; we are done with
388 // crossing as soon as we cross the quality boundary.
389 if (taker.reject(quality))
390 break;
391
392 count++;
393
394 if (use_direct)
395 {
396 if (auto stream = j_.debug())
397 {
398 stream << count << " Direct:";
399 stream << " offer: " << offers_direct.tip();
400 stream << " in: " << offers_direct.tip().amount().in;
401 stream << " out: " << offers_direct.tip().amount().out;
402 stream << " owner: " << offers_direct.tip().owner();
403 stream << " funds: "
404 << accountFunds(
405 view,
406 offers_direct.tip().owner(),
407 offers_direct.tip().amount().out,
409 viewJ);
410 }
411
412 cross_result = taker.cross(offers_direct.tip());
413
414 JLOG(j_.debug()) << "Direct Result: " << transToken(cross_result);
415
416 if (dry_offer(view, offers_direct.tip()))
417 {
418 direct_consumed = true;
419 have_direct = step_account(offers_direct, taker);
420 }
421 }
422 else
423 {
424 if (auto stream = j_.debug())
425 {
426 auto const owner1_funds_before = accountFunds(
427 view,
428 offers_leg1.tip().owner(),
429 offers_leg1.tip().amount().out,
431 viewJ);
432
433 auto const owner2_funds_before = accountFunds(
434 view,
435 offers_leg2.tip().owner(),
436 offers_leg2.tip().amount().out,
438 viewJ);
439
440 stream << count << " Bridge:";
441 stream << " offer1: " << offers_leg1.tip();
442 stream << " in: " << offers_leg1.tip().amount().in;
443 stream << " out: " << offers_leg1.tip().amount().out;
444 stream << " owner: " << offers_leg1.tip().owner();
445 stream << " funds: " << owner1_funds_before;
446 stream << " offer2: " << offers_leg2.tip();
447 stream << " in: " << offers_leg2.tip().amount().in;
448 stream << " out: " << offers_leg2.tip().amount().out;
449 stream << " owner: " << offers_leg2.tip().owner();
450 stream << " funds: " << owner2_funds_before;
451 }
452
453 cross_result = taker.cross(offers_leg1.tip(), offers_leg2.tip());
454
455 JLOG(j_.debug()) << "Bridge Result: " << transToken(cross_result);
456
457 if (view.rules().enabled(fixTakerDryOfferRemoval))
458 {
459 // have_bridge can be true the next time 'round only if
460 // neither of the OfferStreams are dry.
461 leg1_consumed = dry_offer(view, offers_leg1.tip());
462 if (leg1_consumed)
463 have_bridge &= offers_leg1.step();
464
465 leg2_consumed = dry_offer(view, offers_leg2.tip());
466 if (leg2_consumed)
467 have_bridge &= offers_leg2.step();
468 }
469 else
470 {
471 // This old behavior may leave an empty offer in the book for
472 // the second leg.
473 if (dry_offer(view, offers_leg1.tip()))
474 {
475 leg1_consumed = true;
476 have_bridge = (have_bridge && offers_leg1.step());
477 }
478 if (dry_offer(view, offers_leg2.tip()))
479 {
480 leg2_consumed = true;
481 have_bridge = (have_bridge && offers_leg2.step());
482 }
483 }
484 }
485
486 if (cross_result != tesSUCCESS)
487 {
488 cross_result = tecFAILED_PROCESSING;
489 break;
490 }
491
492 if (taker.done())
493 {
494 JLOG(j_.debug()) << "The taker reports he's done during crossing!";
495 break;
496 }
497
498 if (reachedOfferCrossingLimit(taker))
499 {
500 JLOG(j_.debug()) << "The offer crossing limit has been exceeded!";
501 break;
502 }
503
504 // Postcondition: If we aren't done, then we *must* have consumed at
505 // least one offer fully.
506 XRPL_ASSERT(
507 direct_consumed || leg1_consumed || leg2_consumed,
508 "ripple::CreateOffer::bridged_cross : consumed an offer");
509
510 if (!direct_consumed && !leg1_consumed && !leg2_consumed)
511 Throw<std::logic_error>(
512 "bridged crossing: nothing was fully consumed.");
513 }
514
515 return std::make_pair(cross_result, taker.remaining_offer());
516}
517
520 Taker& taker,
521 ApplyView& view,
522 ApplyView& view_cancel,
523 NetClock::time_point const when)
524{
525 OfferStream offers(
526 view,
527 view_cancel,
528 Book(taker.issue_in(), taker.issue_out()),
529 when,
531 j_);
532
533 TER cross_result(tesSUCCESS);
534 int count = 0;
535
536 bool have_offer = step_account(offers, taker);
537
538 // Modifying the order or logic of the operations in the loop will cause
539 // a protocol breaking change.
540 while (have_offer)
541 {
542 bool direct_consumed = false;
543 auto& offer(offers.tip());
544
545 // We are done with crossing as soon as we cross the quality boundary
546 if (taker.reject(offer.quality()))
547 break;
548
549 count++;
550
551 if (auto stream = j_.debug())
552 {
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();
559 stream << " funds: "
560 << accountFunds(
561 view,
562 offer.owner(),
563 offer.amount().out,
565 ctx_.app.journal("View"));
566 }
567
568 cross_result = taker.cross(offer);
569
570 JLOG(j_.debug()) << "Direct Result: " << transToken(cross_result);
571
572 if (dry_offer(view, offer))
573 {
574 direct_consumed = true;
575 have_offer = step_account(offers, taker);
576 }
577
578 if (cross_result != tesSUCCESS)
579 {
580 cross_result = tecFAILED_PROCESSING;
581 break;
582 }
583
584 if (taker.done())
585 {
586 JLOG(j_.debug()) << "The taker reports he's done during crossing!";
587 break;
588 }
589
590 if (reachedOfferCrossingLimit(taker))
591 {
592 JLOG(j_.debug()) << "The offer crossing limit has been exceeded!";
593 break;
594 }
595
596 // Postcondition: If we aren't done, then we *must* have consumed the
597 // offer on the books fully!
598 XRPL_ASSERT(
599 direct_consumed,
600 "ripple::CreateOffer::direct_cross : consumed an offer");
601
602 if (!direct_consumed)
603 Throw<std::logic_error>(
604 "direct crossing: nothing was fully consumed.");
605 }
606
607 return std::make_pair(cross_result, taker.remaining_offer());
608}
609
610// Step through the stream for as long as possible, skipping any offers
611// that are from the taker or which cross the taker's threshold.
612// Return false if the is no offer in the book, true otherwise.
613bool
615{
616 while (stream.step())
617 {
618 auto const& offer = stream.tip();
619
620 // This offer at the tip crosses the taker's threshold. We're done.
621 if (taker.reject(offer.quality()))
622 return true;
623
624 // This offer at the tip is not from the taker. We're done.
625 if (offer.owner() != taker.account())
626 return true;
627 }
628
629 // We ran out of offers. Can't advance.
630 return false;
631}
632
633// Fill as much of the offer as possible by consuming offers
634// already on the books. Return the status and the amount of
635// the offer to left unfilled.
638 Sandbox& sb,
639 Sandbox& sbCancel,
640 Amounts const& takerAmount)
641{
642 NetClock::time_point const when = sb.parentCloseTime();
643
644 beast::WrappedSink takerSink(j_, "Taker ");
645
646 Taker taker(
648 sb,
649 account_,
650 takerAmount,
651 ctx_.tx.getFlags(),
652 beast::Journal(takerSink));
653
654 // If the taker is unfunded before we begin crossing
655 // there's nothing to do - just return an error.
656 //
657 // We check this in preclaim, but when selling XRP
658 // charged fees can cause a user's available balance
659 // to go to 0 (by causing it to dip below the reserve)
660 // so we check this case again.
661 if (taker.unfunded())
662 {
663 JLOG(j_.debug()) << "Not crossing: taker is unfunded.";
664 return {tecUNFUNDED_OFFER, takerAmount};
665 }
666
667 try
668 {
670 return bridged_cross(taker, sb, sbCancel, when);
671
672 return direct_cross(taker, sb, sbCancel, when);
673 }
674 catch (std::exception const& e)
675 {
676 JLOG(j_.error()) << "Exception during offer crossing: " << e.what();
677 return {tecINTERNAL, taker.remaining_offer()};
678 }
679}
680
683 PaymentSandbox& psb,
684 PaymentSandbox& psbCancel,
685 Amounts const& takerAmount)
686{
687 try
688 {
689 // If the taker is unfunded before we begin crossing there's nothing
690 // to do - just return an error.
691 //
692 // We check this in preclaim, but when selling XRP charged fees can
693 // cause a user's available balance to go to 0 (by causing it to dip
694 // below the reserve) so we check this case again.
695 STAmount const inStartBalance =
696 accountFunds(psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);
697 if (inStartBalance <= beast::zero)
698 {
699 // The account balance can't cover even part of the offer.
700 JLOG(j_.debug()) << "Not crossing: taker is unfunded.";
701 return {tecUNFUNDED_OFFER, takerAmount};
702 }
703
704 // If the gateway has a transfer rate, accommodate that. The
705 // gateway takes its cut without any special consent from the
706 // offer taker. Set sendMax to allow for the gateway's cut.
707 Rate gatewayXferRate{QUALITY_ONE};
708 STAmount sendMax = takerAmount.in;
709 if (!sendMax.native() && (account_ != sendMax.getIssuer()))
710 {
711 gatewayXferRate = transferRate(psb, sendMax.getIssuer());
712 if (gatewayXferRate.value != QUALITY_ONE)
713 {
714 sendMax = multiplyRound(
715 takerAmount.in,
716 gatewayXferRate,
717 takerAmount.in.issue(),
718 true);
719 }
720 }
721
722 // Payment flow code compares quality after the transfer rate is
723 // included. Since transfer rate is incorporated compute threshold.
724 Quality threshold{takerAmount.out, sendMax};
725
726 // If we're creating a passive offer adjust the threshold so we only
727 // cross offers that have a better quality than this one.
728 std::uint32_t const txFlags = ctx_.tx.getFlags();
729 if (txFlags & tfPassive)
730 ++threshold;
731
732 // Don't send more than our balance.
733 if (sendMax > inStartBalance)
734 sendMax = inStartBalance;
735
736 // Always invoke flow() with the default path. However if neither
737 // of the takerAmount currencies are XRP then we cross through an
738 // additional path with XRP as the intermediate between two books.
739 // This second path we have to build ourselves.
740 STPathSet paths;
741 if (!takerAmount.in.native() && !takerAmount.out.native())
742 {
743 STPath path;
744 path.emplace_back(std::nullopt, xrpCurrency(), std::nullopt);
745 paths.emplace_back(std::move(path));
746 }
747 // Special handling for the tfSell flag.
748 STAmount deliver = takerAmount.out;
749 OfferCrossing offerCrossing = OfferCrossing::yes;
750 if (txFlags & tfSell)
751 {
752 offerCrossing = OfferCrossing::sell;
753 // We are selling, so we will accept *more* than the offer
754 // specified. Since we don't know how much they might offer,
755 // we allow delivery of the largest possible amount.
756 if (deliver.native())
758 else
759 // We can't use the maximum possible currency here because
760 // there might be a gateway transfer rate to account for.
761 // Since the transfer rate cannot exceed 200%, we use 1/2
762 // maxValue for our limit.
763 deliver = STAmount{
764 takerAmount.out.issue(),
767 }
768
769 // Call the payment engine's flow() to do the actual work.
770 auto const result = flow(
771 psb,
772 deliver,
773 account_,
774 account_,
775 paths,
776 true, // default path
777 !(txFlags & tfFillOrKill), // partial payment
778 true, // owner pays transfer fee
779 offerCrossing,
780 threshold,
781 sendMax,
782 j_);
783
784 // If stale offers were found remove them.
785 for (auto const& toRemove : result.removableOffers)
786 {
787 if (auto otr = psb.peek(keylet::offer(toRemove)))
788 offerDelete(psb, otr, j_);
789 if (auto otr = psbCancel.peek(keylet::offer(toRemove)))
790 offerDelete(psbCancel, otr, j_);
791 }
792
793 // Determine the size of the final offer after crossing.
794 auto afterCross = takerAmount; // If !tesSUCCESS offer unchanged
795 if (isTesSuccess(result.result()))
796 {
797 STAmount const takerInBalance = accountFunds(
798 psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);
799
800 if (takerInBalance <= beast::zero)
801 {
802 // If offer crossing exhausted the account's funds don't
803 // create the offer.
804 afterCross.in.clear();
805 afterCross.out.clear();
806 }
807 else
808 {
809 STAmount const rate{
810 Quality{takerAmount.out, takerAmount.in}.rate()};
811
812 if (txFlags & tfSell)
813 {
814 // If selling then scale the new out amount based on how
815 // much we sold during crossing. This preserves the offer
816 // Quality,
817
818 // Reduce the offer that is placed by the crossed amount.
819 // Note that we must ignore the portion of the
820 // actualAmountIn that may have been consumed by a
821 // gateway's transfer rate.
822 STAmount nonGatewayAmountIn = result.actualAmountIn;
823 if (gatewayXferRate.value != QUALITY_ONE)
824 nonGatewayAmountIn = divideRound(
825 result.actualAmountIn,
826 gatewayXferRate,
827 takerAmount.in.issue(),
828 true);
829
830 afterCross.in -= nonGatewayAmountIn;
831
832 // It's possible that the divRound will cause our subtract
833 // to go slightly negative. So limit afterCross.in to zero.
834 if (afterCross.in < beast::zero)
835 // We should verify that the difference *is* small, but
836 // what is a good threshold to check?
837 afterCross.in.clear();
838
839 afterCross.out = [&]() {
840 // Careful analysis showed that rounding up this
841 // divRound result could lead to placing a reduced
842 // offer in the ledger that blocks order books. So
843 // the fixReducedOffersV1 amendment changes the
844 // behavior to round down instead.
845 if (psb.rules().enabled(fixReducedOffersV1))
846 return divRoundStrict(
847 afterCross.in,
848 rate,
849 takerAmount.out.issue(),
850 false);
851
852 return divRound(
853 afterCross.in, rate, takerAmount.out.issue(), true);
854 }();
855 }
856 else
857 {
858 // If not selling, we scale the input based on the
859 // remaining output. This too preserves the offer
860 // Quality.
861 afterCross.out -= result.actualAmountOut;
862 XRPL_ASSERT(
863 afterCross.out >= beast::zero,
864 "ripple::CreateOffer::flowCross : minimum offer");
865 if (afterCross.out < beast::zero)
866 afterCross.out.clear();
867 afterCross.in = mulRound(
868 afterCross.out, rate, takerAmount.in.issue(), true);
869 }
870 }
871 }
872
873 // Return how much of the offer is left.
874 return {tesSUCCESS, afterCross};
875 }
876 catch (std::exception const& e)
877 {
878 JLOG(j_.error()) << "Exception during offer crossing: " << e.what();
879 }
880 return {tecINTERNAL, takerAmount};
881}
882
884CreateOffer::cross(Sandbox& sb, Sandbox& sbCancel, Amounts const& takerAmount)
885{
886 if (sb.rules().enabled(featureFlowCross))
887 {
888 PaymentSandbox psbFlow{&sb};
889 PaymentSandbox psbCancelFlow{&sbCancel};
890 auto const ret = flowCross(psbFlow, psbCancelFlow, takerAmount);
891 psbFlow.apply(sb);
892 psbCancelFlow.apply(sbCancel);
893 return ret;
894 }
895
896 Sandbox sbTaker{&sb};
897 Sandbox sbCancelTaker{&sbCancel};
898 auto const ret = takerCross(sbTaker, sbCancelTaker, takerAmount);
899 sbTaker.apply(sb);
900 sbCancelTaker.apply(sbCancel);
901 return ret;
902}
903
906{
907 std::string txt = amount.getText();
908 txt += "/";
909 txt += to_string(amount.issue().currency);
910 return txt;
911}
912
913void
915{
917 bool const pays_xrp = ctx_.tx.getFieldAmount(sfTakerPays).native();
918 bool const gets_xrp = ctx_.tx.getFieldAmount(sfTakerGets).native();
919 if (pays_xrp && !gets_xrp)
921 else if (gets_xrp && !pays_xrp)
923
924 return Transactor::preCompute();
925}
926
929{
930 using beast::zero;
931
932 std::uint32_t const uTxFlags = ctx_.tx.getFlags();
933
934 bool const bPassive(uTxFlags & tfPassive);
935 bool const bImmediateOrCancel(uTxFlags & tfImmediateOrCancel);
936 bool const bFillOrKill(uTxFlags & tfFillOrKill);
937 bool const bSell(uTxFlags & tfSell);
938
939 auto saTakerPays = ctx_.tx[sfTakerPays];
940 auto saTakerGets = ctx_.tx[sfTakerGets];
941
942 auto const cancelSequence = ctx_.tx[~sfOfferSequence];
943
944 // Note that we we use the value from the sequence or ticket as the
945 // offer sequence. For more explanation see comments in SeqProxy.h.
946 auto const offerSequence = ctx_.tx.getSeqProxy().value();
947
948 // This is the original rate of the offer, and is the rate at which
949 // it will be placed, even if crossing offers change the amounts that
950 // end up on the books.
951 auto uRate = getRate(saTakerGets, saTakerPays);
952
953 auto viewJ = ctx_.app.journal("View");
954
955 TER result = tesSUCCESS;
956
957 // Process a cancellation request that's passed along with an offer.
958 if (cancelSequence)
959 {
960 auto const sleCancel =
961 sb.peek(keylet::offer(account_, *cancelSequence));
962
963 // It's not an error to not find the offer to cancel: it might have
964 // been consumed or removed. If it is found, however, it's an error
965 // to fail to delete it.
966 if (sleCancel)
967 {
968 JLOG(j_.debug()) << "Create cancels order " << *cancelSequence;
969 result = offerDelete(sb, sleCancel, viewJ);
970 }
971 }
972
973 auto const expiration = ctx_.tx[~sfExpiration];
974
975 if (hasExpired(sb, expiration))
976 {
977 // If the offer has expired, the transaction has successfully
978 // done nothing, so short circuit from here.
979 //
980 // The return code change is attached to featureDepositPreauth as a
981 // convenience. The change is not big enough to deserve a fix code.
982 TER const ter{
983 sb.rules().enabled(featureDepositPreauth) ? TER{tecEXPIRED}
984 : TER{tesSUCCESS}};
985 return {ter, true};
986 }
987
988 bool const bOpenLedger = sb.open();
989 bool crossed = false;
990
991 if (result == tesSUCCESS)
992 {
993 // If a tick size applies, round the offer to the tick size
994 auto const& uPaysIssuerID = saTakerPays.getIssuer();
995 auto const& uGetsIssuerID = saTakerGets.getIssuer();
996
997 std::uint8_t uTickSize = Quality::maxTickSize;
998 if (!isXRP(uPaysIssuerID))
999 {
1000 auto const sle = sb.read(keylet::account(uPaysIssuerID));
1001 if (sle && sle->isFieldPresent(sfTickSize))
1002 uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
1003 }
1004 if (!isXRP(uGetsIssuerID))
1005 {
1006 auto const sle = sb.read(keylet::account(uGetsIssuerID));
1007 if (sle && sle->isFieldPresent(sfTickSize))
1008 uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
1009 }
1010 if (uTickSize < Quality::maxTickSize)
1011 {
1012 auto const rate =
1013 Quality{saTakerGets, saTakerPays}.round(uTickSize).rate();
1014
1015 // We round the side that's not exact,
1016 // just as if the offer happened to execute
1017 // at a slightly better (for the placer) rate
1018 if (bSell)
1019 {
1020 // this is a sell, round taker pays
1021 saTakerPays = multiply(saTakerGets, rate, saTakerPays.issue());
1022 }
1023 else
1024 {
1025 // this is a buy, round taker gets
1026 saTakerGets = divide(saTakerPays, rate, saTakerGets.issue());
1027 }
1028 if (!saTakerGets || !saTakerPays)
1029 {
1030 JLOG(j_.debug()) << "Offer rounded to zero";
1031 return {result, true};
1032 }
1033
1034 uRate = getRate(saTakerGets, saTakerPays);
1035 }
1036
1037 // We reverse pays and gets because during crossing we are taking.
1038 Amounts const takerAmount(saTakerGets, saTakerPays);
1039
1040 // The amount of the offer that is unfilled after crossing has been
1041 // performed. It may be equal to the original amount (didn't cross),
1042 // empty (fully crossed), or something in-between.
1043 Amounts place_offer;
1044
1045 JLOG(j_.debug()) << "Attempting cross: "
1046 << to_string(takerAmount.in.issue()) << " -> "
1047 << to_string(takerAmount.out.issue());
1048
1049 if (auto stream = j_.trace())
1050 {
1051 stream << " mode: " << (bPassive ? "passive " : "")
1052 << (bSell ? "sell" : "buy");
1053 stream << " in: " << format_amount(takerAmount.in);
1054 stream << " out: " << format_amount(takerAmount.out);
1055 }
1056
1057 std::tie(result, place_offer) = cross(sb, sbCancel, takerAmount);
1058
1059 // We expect the implementation of cross to succeed
1060 // or give a tec.
1061 XRPL_ASSERT(
1062 result == tesSUCCESS || isTecClaim(result),
1063 "ripple::CreateOffer::applyGuts : result is tesSUCCESS or "
1064 "tecCLAIM");
1065
1066 if (auto stream = j_.trace())
1067 {
1068 stream << "Cross result: " << transToken(result);
1069 stream << " in: " << format_amount(place_offer.in);
1070 stream << " out: " << format_amount(place_offer.out);
1071 }
1072
1073 if (result == tecFAILED_PROCESSING && bOpenLedger)
1074 result = telFAILED_PROCESSING;
1075
1076 if (result != tesSUCCESS)
1077 {
1078 JLOG(j_.debug()) << "final result: " << transToken(result);
1079 return {result, true};
1080 }
1081
1082 XRPL_ASSERT(
1083 saTakerGets.issue() == place_offer.in.issue(),
1084 "ripple::CreateOffer::applyGuts : taker gets issue match");
1085 XRPL_ASSERT(
1086 saTakerPays.issue() == place_offer.out.issue(),
1087 "ripple::CreateOffer::applyGuts : taker pays issue match");
1088
1089 if (takerAmount != place_offer)
1090 crossed = true;
1091
1092 // The offer that we need to place after offer crossing should
1093 // never be negative. If it is, something went very very wrong.
1094 if (place_offer.in < zero || place_offer.out < zero)
1095 {
1096 JLOG(j_.fatal()) << "Cross left offer negative!"
1097 << " in: " << format_amount(place_offer.in)
1098 << " out: " << format_amount(place_offer.out);
1099 return {tefINTERNAL, true};
1100 }
1101
1102 if (place_offer.in == zero || place_offer.out == zero)
1103 {
1104 JLOG(j_.debug()) << "Offer fully crossed!";
1105 return {result, true};
1106 }
1107
1108 // We now need to adjust the offer to reflect the amount left after
1109 // crossing. We reverse in and out here, since during crossing we
1110 // were the taker.
1111 saTakerPays = place_offer.out;
1112 saTakerGets = place_offer.in;
1113 }
1114
1115 XRPL_ASSERT(
1116 saTakerPays > zero && saTakerGets > zero,
1117 "ripple::CreateOffer::applyGuts : taker pays and gets positive");
1118
1119 if (result != tesSUCCESS)
1120 {
1121 JLOG(j_.debug()) << "final result: " << transToken(result);
1122 return {result, true};
1123 }
1124
1125 if (auto stream = j_.trace())
1126 {
1127 stream << "Place" << (crossed ? " remaining " : " ") << "offer:";
1128 stream << " Pays: " << saTakerPays.getFullText();
1129 stream << " Gets: " << saTakerGets.getFullText();
1130 }
1131
1132 // For 'fill or kill' offers, failure to fully cross means that the
1133 // entire operation should be aborted, with only fees paid.
1134 if (bFillOrKill)
1135 {
1136 JLOG(j_.trace()) << "Fill or Kill: offer killed";
1137 if (sb.rules().enabled(fix1578))
1138 return {tecKILLED, false};
1139 return {tesSUCCESS, false};
1140 }
1141
1142 // For 'immediate or cancel' offers, the amount remaining doesn't get
1143 // placed - it gets canceled and the operation succeeds.
1144 if (bImmediateOrCancel)
1145 {
1146 JLOG(j_.trace()) << "Immediate or cancel: offer canceled";
1147 if (!crossed && sb.rules().enabled(featureImmediateOfferKilled))
1148 // If the ImmediateOfferKilled amendment is enabled, any
1149 // ImmediateOrCancel offer that transfers absolutely no funds
1150 // returns tecKILLED rather than tesSUCCESS. Motivation for the
1151 // change is here: https://github.com/ripple/rippled/issues/4115
1152 return {tecKILLED, false};
1153 return {tesSUCCESS, true};
1154 }
1155
1156 auto const sleCreator = sb.peek(keylet::account(account_));
1157 if (!sleCreator)
1158 return {tefINTERNAL, false};
1159
1160 {
1161 XRPAmount reserve =
1162 sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1);
1163
1164 if (mPriorBalance < reserve)
1165 {
1166 // If we are here, the signing account had an insufficient reserve
1167 // *prior* to our processing. If something actually crossed, then
1168 // we allow this; otherwise, we just claim a fee.
1169 if (!crossed)
1170 result = tecINSUF_RESERVE_OFFER;
1171
1172 if (result != tesSUCCESS)
1173 {
1174 JLOG(j_.debug()) << "final result: " << transToken(result);
1175 }
1176
1177 return {result, true};
1178 }
1179 }
1180
1181 // We need to place the remainder of the offer into its order book.
1182 auto const offer_index = keylet::offer(account_, offerSequence);
1183
1184 // Add offer to owner's directory.
1185 auto const ownerNode = sb.dirInsert(
1187
1188 if (!ownerNode)
1189 {
1190 JLOG(j_.debug())
1191 << "final result: failed to add offer to owner's directory";
1192 return {tecDIR_FULL, true};
1193 }
1194
1195 // Update owner count.
1196 adjustOwnerCount(sb, sleCreator, 1, viewJ);
1197
1198 JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue())
1199 << " : " << to_string(saTakerGets.issue());
1200
1201 Book const book{saTakerPays.issue(), saTakerGets.issue()};
1202
1203 // Add offer to order book, using the original rate
1204 // before any crossing occured.
1205 auto dir = keylet::quality(keylet::book(book), uRate);
1206 bool const bookExisted = static_cast<bool>(sb.peek(dir));
1207
1208 auto const bookNode = sb.dirAppend(dir, offer_index, [&](SLE::ref sle) {
1209 sle->setFieldH160(sfTakerPaysCurrency, saTakerPays.issue().currency);
1210 sle->setFieldH160(sfTakerPaysIssuer, saTakerPays.issue().account);
1211 sle->setFieldH160(sfTakerGetsCurrency, saTakerGets.issue().currency);
1212 sle->setFieldH160(sfTakerGetsIssuer, saTakerGets.issue().account);
1213 sle->setFieldU64(sfExchangeRate, uRate);
1214 });
1215
1216 if (!bookNode)
1217 {
1218 JLOG(j_.debug()) << "final result: failed to add offer to book";
1219 return {tecDIR_FULL, true};
1220 }
1221
1222 auto sleOffer = std::make_shared<SLE>(offer_index);
1223 sleOffer->setAccountID(sfAccount, account_);
1224 sleOffer->setFieldU32(sfSequence, offerSequence);
1225 sleOffer->setFieldH256(sfBookDirectory, dir.key);
1226 sleOffer->setFieldAmount(sfTakerPays, saTakerPays);
1227 sleOffer->setFieldAmount(sfTakerGets, saTakerGets);
1228 sleOffer->setFieldU64(sfOwnerNode, *ownerNode);
1229 sleOffer->setFieldU64(sfBookNode, *bookNode);
1230 if (expiration)
1231 sleOffer->setFieldU32(sfExpiration, *expiration);
1232 if (bPassive)
1233 sleOffer->setFlag(lsfPassive);
1234 if (bSell)
1235 sleOffer->setFlag(lsfSell);
1236 sb.insert(sleOffer);
1237
1238 if (!bookExisted)
1240
1241 JLOG(j_.debug()) << "final result: success";
1242
1243 return {tesSUCCESS, true};
1244}
1245
1246TER
1248{
1249 // This is the ledger view that we work against. Transactions are applied
1250 // as we go on processing transactions.
1251 Sandbox sb(&ctx_.view());
1252
1253 // This is a ledger with just the fees paid and any unfunded or expired
1254 // offers we encounter removed. It's used when handling Fill-or-Kill offers,
1255 // if the order isn't going to be placed, to avoid wasting the work we did.
1256 Sandbox sbCancel(&ctx_.view());
1257
1258 auto const result = applyGuts(sb, sbCancel);
1259 if (result.second)
1260 sb.apply(ctx_.rawView());
1261 else
1262 sbCancel.apply(ctx_.rawView());
1263 return result.first;
1264}
1265
1266} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:59
Stream fatal() const
Definition: Journal.h:341
Stream error() const
Definition: Journal.h:335
Stream debug() const
Definition: Journal.h:317
Stream trace() const
Severity stream access functions.
Definition: Journal.h:311
Wraps a Journal::Sink to prefix its output with a string.
Definition: WrappedSink.h:34
virtual beast::Journal journal(std::string const &name)=0
virtual OrderBookDB & getOrderBookDB()=0
RawView & rawView()
Definition: ApplyContext.h:67
ApplyView & view()
Definition: ApplyContext.h:54
Application & app
Definition: ApplyContext.h:47
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:140
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.
Definition: ApplyView.h:277
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.
Definition: ApplyView.h:314
bool done() const
Returns true if order crossing should not continue.
Definition: Taker.cpp:122
AccountID const & account() const noexcept
Returns the account identifier of the taker.
Definition: Taker.h:172
bool reject(Quality const &quality) const noexcept
Returns true if the quality does not meet the taker's requirements.
Definition: Taker.h:179
Issue const & issue_in() const
Returns the Issue associated with the input of the offer.
Definition: Taker.h:193
Amounts remaining_offer() const
Returns the amount remaining on the offer.
Definition: Taker.cpp:151
Amounts const & original_offer() const
Returns the amount that the offer was originally placed at.
Definition: Taker.cpp:184
Issue const & issue_out() const
Returns the Issue associated with the output of the offer.
Definition: Taker.h:200
bool unfunded() const
Returns true if the taker has run out of funds.
Definition: Taker.cpp:112
Specifies an order book.
Definition: Book.h:34
std::pair< TER, Amounts > bridged_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
OfferStream::StepCounter stepCounter_
Definition: CreateOffer.h:142
std::pair< TER, Amounts > flowCross(PaymentSandbox &psb, PaymentSandbox &psbCancel, Amounts const &takerAmount)
static TER checkAcceptAsset(ReadView const &view, ApplyFlags const flags, AccountID const id, beast::Journal const j, Issue const &issue)
void preCompute() override
Gather information beyond what the Transactor base class gathers.
bool dry_offer(ApplyView &view, Offer const &offer)
static TER preclaim(PreclaimContext const &ctx)
Enforce constraints beyond those of the Transactor base class.
static bool step_account(OfferStream &stream, Taker const &taker)
static std::string format_amount(STAmount const &amount)
bool reachedOfferCrossingLimit(Taker const &taker) const
std::pair< TER, Amounts > takerCross(Sandbox &sb, Sandbox &sbCancel, Amounts const &takerAmount)
std::pair< TER, Amounts > direct_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
static NotTEC preflight(PreflightContext const &ctx)
Enforce constraints beyond those of the Transactor base class.
Definition: CreateOffer.cpp:43
static TxConsequences makeTxConsequences(PreflightContext const &ctx)
Definition: CreateOffer.cpp:32
CrossType cross_type_
Definition: CreateOffer.h:139
TER doApply() override
Precondition: fee collection is likely.
std::pair< TER, bool > applyGuts(Sandbox &view, Sandbox &view_cancel)
static std::pair< bool, Quality > select_path(bool have_direct, OfferStream const &direct, bool have_bridge, OfferStream const &leg1, OfferStream const &leg2)
std::pair< TER, Amounts > cross(Sandbox &sb, Sandbox &sbCancel, Amounts const &takerAmount)
A currency issued by an account.
Definition: Issue.h:36
AccountID account
Definition: Issue.h:39
Currency currency
Definition: Issue.h:38
Presents and consumes the offers in an order book.
Definition: OfferStream.h:148
void addOrderBook(Book const &)
A wrapper which makes credits unavailable to balances.
A view into a ledger.
Definition: ReadView.h:55
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition: ReadView.h:115
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:122
Currency const & getCurrency() const
Definition: STAmount.h:493
static const std::uint64_t cMaxNative
Definition: STAmount.h:71
static const int cMaxOffset
Definition: STAmount.h:66
std::string getText() const override
Definition: STAmount.cpp:515
AccountID const & getIssuer() const
Definition: STAmount.h:499
Issue const & issue() const
Definition: STAmount.h:487
bool native() const noexcept
Definition: STAmount.h:449
static const std::uint64_t cMaxValue
Definition: STAmount.h:70
STAmount const & getFieldAmount(SField const &field) const
Definition: STObject.cpp:635
std::uint32_t getFlags() const
Definition: STObject.cpp:507
void emplace_back(Args &&... args)
Definition: STPathSet.h:520
SeqProxy getSeqProxy() const
Definition: STTx.cpp:186
Discardable, editable view to a ledger.
Definition: Sandbox.h:35
void apply(RawView &to)
Definition: Sandbox.h:55
constexpr std::uint32_t value() const
Definition: SeqProxy.h:82
bool step()
Advance to the next valid offer.
TOffer< TIn, TOut > & tip() const
Returns the offer at the tip of the order book.
Definition: OfferStream.h:108
Quality quality() const noexcept
Returns the quality of the offer.
Definition: Offer.h:76
AccountID const & owner() const
Returns the account id of the offer's owner.
Definition: Offer.h:83
TAmounts< TIn, TOut > const & amount() const
Returns the in and out amounts.
Definition: Offer.h:92
std::uint32_t get_bridge_crossings() const
Definition: Taker.h:274
std::uint32_t get_direct_crossings() const
Definition: Taker.h:268
TER cross(Offer &offer)
Perform a direct or bridged offer crossing as appropriate.
Definition: Taker.cpp:821
AccountID const account_
Definition: Transactor.h:91
ApplyView & view()
Definition: Transactor.h:107
beast::Journal const j_
Definition: Transactor.h:89
XRPAmount mPriorBalance
Definition: Transactor.h:92
virtual void preCompute()
Definition: Transactor.cpp:470
ApplyContext & ctx_
Definition: Transactor.h:88
Class describing the consequences to the account of applying a transaction if the transaction consume...
Definition: applySteps.h:58
Fees const & fees() const override
Returns the fees for the base ledger.
bool open() const override
Returns true if this reflects an open ledger.
void insert(std::shared_ptr< SLE > const &sle) override
Insert a new state SLE.
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Rules const & rules() const override
Returns the tx processing rules.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
T make_pair(T... args)
T min(T... args)
Keylet quality(Keylet const &k, std::uint64_t q) noexcept
The initial directory page for a specific quality.
Definition: Indexes.cpp:256
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition: Indexes.cpp:220
static book_t const book
Definition: Indexes.h:104
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:160
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:350
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:250
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:118
STAmount divide(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:87
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
Definition: UintTypes.cpp:129
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition: View.cpp:366
@ fhZERO_IF_FROZEN
Definition: View.h:80
@ fhIGNORE_FREEZE
Definition: View.h:80
bool isTecClaim(TER x)
Definition: TER.h:662
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
@ telFAILED_PROCESSING
Definition: TER.h:56
constexpr std::uint32_t tfOfferCreateMask
Definition: TxFlags.h:100
STAmount divRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
Definition: STAmount.cpp:1627
bool isLegalNet(STAmount const &value)
Definition: STAmount.h:581
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition: View.cpp:573
@ lsfRequireAuth
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:47
bool isTesSuccess(TER x)
Definition: TER.h:656
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition: View.cpp:848
constexpr std::uint32_t tfFillOrKill
Definition: TxFlags.h:98
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:82
STAmount divideRound(STAmount const &amount, Rate const &rate, bool roundUp)
Definition: Rate2.cpp:98
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
Definition: StrandFlow.h:106
constexpr std::uint32_t tfPassive
Definition: TxFlags.h:96
constexpr std::uint32_t tfImmediateOrCancel
Definition: TxFlags.h:97
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition: View.cpp:1010
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition: STAmount.cpp:451
@ tefINTERNAL
Definition: TER.h:173
OfferCrossing
Definition: Steps.h:42
@ yes
Definition: Steps.h:42
@ sell
Definition: Steps.h:42
Quality composed_quality(Quality const &lhs, Quality const &rhs)
Definition: Quality.cpp:153
static bool adjustOwnerCount(ApplyContext &ctx, int count)
Definition: SetOracle.cpp:186
std::string transToken(TER code)
Definition: TER.cpp:251
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:134
Currency const & xrpCurrency()
XRP currency.
Definition: UintTypes.cpp:115
@ tecINSUF_RESERVE_OFFER
Definition: TER.h:276
@ tecNO_ISSUER
Definition: TER.h:286
@ tecDIR_FULL
Definition: TER.h:274
@ tecUNFUNDED_OFFER
Definition: TER.h:271
@ tecFROZEN
Definition: TER.h:290
@ tecKILLED
Definition: TER.h:303
@ tecINTERNAL
Definition: TER.h:297
@ tecNO_LINE
Definition: TER.h:288
@ tecFAILED_PROCESSING
Definition: TER.h:273
@ tecEXPIRED
Definition: TER.h:301
@ tecNO_AUTH
Definition: TER.h:287
@ tesSUCCESS
Definition: TER.h:242
STAmount divRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
Definition: STAmount.cpp:1617
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
STAmount mulRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
Definition: STAmount.cpp:1510
STAmount multiplyRound(STAmount const &amount, Rate const &rate, bool roundUp)
Definition: Rate2.cpp:58
ApplyFlags
Definition: ApplyView.h:30
@ tapRETRY
Definition: ApplyView.h:39
constexpr std::uint32_t tfSell
Definition: TxFlags.h:99
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition: View.cpp:164
@ terNO_ACCOUNT
Definition: TER.h:217
@ terNO_AUTH
Definition: TER.h:218
@ terNO_LINE
Definition: TER.h:219
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Definition: View.cpp:173
@ temBAD_ISSUER
Definition: TER.h:93
@ temBAD_AMOUNT
Definition: TER.h:89
@ temREDUNDANT
Definition: TER.h:112
@ temBAD_CURRENCY
Definition: TER.h:90
@ temBAD_SEQUENCE
Definition: TER.h:104
@ temBAD_EXPIRATION
Definition: TER.h:91
@ temBAD_OFFER
Definition: TER.h:95
@ temINVALID_FLAG
Definition: TER.h:111
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
Definition: protocol/Fees.h:49
State information when determining if a tx is likely to claim a fee.
Definition: Transactor.h:53
ReadView const & view
Definition: Transactor.h:56
Application & app
Definition: Transactor.h:55
beast::Journal const j
Definition: Transactor.h:60
State information when preflighting a tx.
Definition: Transactor.h:32
beast::Journal const j
Definition: Transactor.h:38
Represents a transfer rate.
Definition: Rate.h:38
T tie(T... args)
T what(T... args)