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 // An account can not create a trustline to itself, so no line can exist
263 // to be frozen. Additionally, an issuer can always accept its own
264 // issuance.
265 if (issue.account == id)
266 {
267 return tesSUCCESS;
268 }
269
270 auto const trustLine =
271 view.read(keylet::line(id, issue.account, issue.currency));
272
273 if (!trustLine)
274 {
275 return tesSUCCESS;
276 }
277
278 // There's no difference which side enacted deep freeze, accepting
279 // tokens shouldn't be possible.
280 bool const deepFrozen =
281 (*trustLine)[sfFlags] & (lsfLowDeepFreeze | lsfHighDeepFreeze);
282
283 if (deepFrozen)
284 {
285 return tecFROZEN;
286 }
287
288 return tesSUCCESS;
289}
290
291bool
293{
294 if (offer.fully_consumed())
295 return true;
296 auto const amount = accountFunds(
297 view,
298 offer.owner(),
299 offer.amount().out,
301 ctx_.app.journal("View"));
302 return (amount <= beast::zero);
303}
304
307 bool have_direct,
308 OfferStream const& direct,
309 bool have_bridge,
310 OfferStream const& leg1,
311 OfferStream const& leg2)
312{
313 // If we don't have any viable path, why are we here?!
314 XRPL_ASSERT(
315 have_direct || have_bridge,
316 "ripple::CreateOffer::select_path : valid inputs");
317
318 // If there's no bridged path, the direct is the best by default.
319 if (!have_bridge)
320 return std::make_pair(true, direct.tip().quality());
321
322 Quality const bridged_quality(
323 composed_quality(leg1.tip().quality(), leg2.tip().quality()));
324
325 if (have_direct)
326 {
327 // We compare the quality of the composed quality of the bridged
328 // offers and compare it against the direct offer to pick the best.
329 Quality const direct_quality(direct.tip().quality());
330
331 if (bridged_quality < direct_quality)
332 return std::make_pair(true, direct_quality);
333 }
334
335 // Either there was no direct offer, or it didn't have a better quality
336 // than the bridge.
337 return std::make_pair(false, bridged_quality);
338}
339
340bool
342{
343 auto const crossings =
344 taker.get_direct_crossings() + (2 * taker.get_bridge_crossings());
345
346 // The crossing limit is part of the Ripple protocol and
347 // changing it is a transaction-processing change.
348 return crossings >= 850;
349}
350
353 Taker& taker,
354 ApplyView& view,
355 ApplyView& view_cancel,
356 NetClock::time_point const when)
357{
358 auto const& takerAmount = taker.original_offer();
359
360 XRPL_ASSERT(
361 !isXRP(takerAmount.in) && !isXRP(takerAmount.out),
362 "ripple::CreateOffer::bridged_cross : neither is XRP");
363
364 if (isXRP(takerAmount.in) || isXRP(takerAmount.out))
365 Throw<std::logic_error>("Bridging with XRP and an endpoint.");
366
367 OfferStream offers_direct(
368 view,
369 view_cancel,
370 Book(taker.issue_in(), taker.issue_out()),
371 when,
373 j_);
374
375 OfferStream offers_leg1(
376 view,
377 view_cancel,
378 Book(taker.issue_in(), xrpIssue()),
379 when,
381 j_);
382
383 OfferStream offers_leg2(
384 view,
385 view_cancel,
386 Book(xrpIssue(), taker.issue_out()),
387 when,
389 j_);
390
391 TER cross_result = tesSUCCESS;
392
393 // Note the subtle distinction here: self-offers encountered in the
394 // bridge are taken, but self-offers encountered in the direct book
395 // are not.
396 bool have_bridge = offers_leg1.step() && offers_leg2.step();
397 bool have_direct = step_account(offers_direct, taker);
398 int count = 0;
399
400 auto viewJ = ctx_.app.journal("View");
401
402 // Modifying the order or logic of the operations in the loop will cause
403 // a protocol breaking change.
404 while (have_direct || have_bridge)
405 {
406 bool leg1_consumed = false;
407 bool leg2_consumed = false;
408 bool direct_consumed = false;
409
410 auto const [use_direct, quality] = select_path(
411 have_direct, offers_direct, have_bridge, offers_leg1, offers_leg2);
412
413 // We are always looking at the best quality; we are done with
414 // crossing as soon as we cross the quality boundary.
415 if (taker.reject(quality))
416 break;
417
418 count++;
419
420 if (use_direct)
421 {
422 if (auto stream = j_.debug())
423 {
424 stream << count << " Direct:";
425 stream << " offer: " << offers_direct.tip();
426 stream << " in: " << offers_direct.tip().amount().in;
427 stream << " out: " << offers_direct.tip().amount().out;
428 stream << " owner: " << offers_direct.tip().owner();
429 stream << " funds: "
430 << accountFunds(
431 view,
432 offers_direct.tip().owner(),
433 offers_direct.tip().amount().out,
435 viewJ);
436 }
437
438 cross_result = taker.cross(offers_direct.tip());
439
440 JLOG(j_.debug()) << "Direct Result: " << transToken(cross_result);
441
442 if (dry_offer(view, offers_direct.tip()))
443 {
444 direct_consumed = true;
445 have_direct = step_account(offers_direct, taker);
446 }
447 }
448 else
449 {
450 if (auto stream = j_.debug())
451 {
452 auto const owner1_funds_before = accountFunds(
453 view,
454 offers_leg1.tip().owner(),
455 offers_leg1.tip().amount().out,
457 viewJ);
458
459 auto const owner2_funds_before = accountFunds(
460 view,
461 offers_leg2.tip().owner(),
462 offers_leg2.tip().amount().out,
464 viewJ);
465
466 stream << count << " Bridge:";
467 stream << " offer1: " << offers_leg1.tip();
468 stream << " in: " << offers_leg1.tip().amount().in;
469 stream << " out: " << offers_leg1.tip().amount().out;
470 stream << " owner: " << offers_leg1.tip().owner();
471 stream << " funds: " << owner1_funds_before;
472 stream << " offer2: " << offers_leg2.tip();
473 stream << " in: " << offers_leg2.tip().amount().in;
474 stream << " out: " << offers_leg2.tip().amount().out;
475 stream << " owner: " << offers_leg2.tip().owner();
476 stream << " funds: " << owner2_funds_before;
477 }
478
479 cross_result = taker.cross(offers_leg1.tip(), offers_leg2.tip());
480
481 JLOG(j_.debug()) << "Bridge Result: " << transToken(cross_result);
482
483 if (view.rules().enabled(fixTakerDryOfferRemoval))
484 {
485 // have_bridge can be true the next time 'round only if
486 // neither of the OfferStreams are dry.
487 leg1_consumed = dry_offer(view, offers_leg1.tip());
488 if (leg1_consumed)
489 have_bridge &= offers_leg1.step();
490
491 leg2_consumed = dry_offer(view, offers_leg2.tip());
492 if (leg2_consumed)
493 have_bridge &= offers_leg2.step();
494 }
495 else
496 {
497 // This old behavior may leave an empty offer in the book for
498 // the second leg.
499 if (dry_offer(view, offers_leg1.tip()))
500 {
501 leg1_consumed = true;
502 have_bridge = (have_bridge && offers_leg1.step());
503 }
504 if (dry_offer(view, offers_leg2.tip()))
505 {
506 leg2_consumed = true;
507 have_bridge = (have_bridge && offers_leg2.step());
508 }
509 }
510 }
511
512 if (cross_result != tesSUCCESS)
513 {
514 cross_result = tecFAILED_PROCESSING;
515 break;
516 }
517
518 if (taker.done())
519 {
520 JLOG(j_.debug()) << "The taker reports he's done during crossing!";
521 break;
522 }
523
524 if (reachedOfferCrossingLimit(taker))
525 {
526 JLOG(j_.debug()) << "The offer crossing limit has been exceeded!";
527 break;
528 }
529
530 // Postcondition: If we aren't done, then we *must* have consumed at
531 // least one offer fully.
532 XRPL_ASSERT(
533 direct_consumed || leg1_consumed || leg2_consumed,
534 "ripple::CreateOffer::bridged_cross : consumed an offer");
535
536 if (!direct_consumed && !leg1_consumed && !leg2_consumed)
537 Throw<std::logic_error>(
538 "bridged crossing: nothing was fully consumed.");
539 }
540
541 return std::make_pair(cross_result, taker.remaining_offer());
542}
543
546 Taker& taker,
547 ApplyView& view,
548 ApplyView& view_cancel,
549 NetClock::time_point const when)
550{
551 OfferStream offers(
552 view,
553 view_cancel,
554 Book(taker.issue_in(), taker.issue_out()),
555 when,
557 j_);
558
559 TER cross_result(tesSUCCESS);
560 int count = 0;
561
562 bool have_offer = step_account(offers, taker);
563
564 // Modifying the order or logic of the operations in the loop will cause
565 // a protocol breaking change.
566 while (have_offer)
567 {
568 bool direct_consumed = false;
569 auto& offer(offers.tip());
570
571 // We are done with crossing as soon as we cross the quality boundary
572 if (taker.reject(offer.quality()))
573 break;
574
575 count++;
576
577 if (auto stream = j_.debug())
578 {
579 stream << count << " Direct:";
580 stream << " offer: " << offer;
581 stream << " in: " << offer.amount().in;
582 stream << " out: " << offer.amount().out;
583 stream << "quality: " << offer.quality();
584 stream << " owner: " << offer.owner();
585 stream << " funds: "
586 << accountFunds(
587 view,
588 offer.owner(),
589 offer.amount().out,
591 ctx_.app.journal("View"));
592 }
593
594 cross_result = taker.cross(offer);
595
596 JLOG(j_.debug()) << "Direct Result: " << transToken(cross_result);
597
598 if (dry_offer(view, offer))
599 {
600 direct_consumed = true;
601 have_offer = step_account(offers, taker);
602 }
603
604 if (cross_result != tesSUCCESS)
605 {
606 cross_result = tecFAILED_PROCESSING;
607 break;
608 }
609
610 if (taker.done())
611 {
612 JLOG(j_.debug()) << "The taker reports he's done during crossing!";
613 break;
614 }
615
616 if (reachedOfferCrossingLimit(taker))
617 {
618 JLOG(j_.debug()) << "The offer crossing limit has been exceeded!";
619 break;
620 }
621
622 // Postcondition: If we aren't done, then we *must* have consumed the
623 // offer on the books fully!
624 XRPL_ASSERT(
625 direct_consumed,
626 "ripple::CreateOffer::direct_cross : consumed an offer");
627
628 if (!direct_consumed)
629 Throw<std::logic_error>(
630 "direct crossing: nothing was fully consumed.");
631 }
632
633 return std::make_pair(cross_result, taker.remaining_offer());
634}
635
636// Step through the stream for as long as possible, skipping any offers
637// that are from the taker or which cross the taker's threshold.
638// Return false if the is no offer in the book, true otherwise.
639bool
641{
642 while (stream.step())
643 {
644 auto const& offer = stream.tip();
645
646 // This offer at the tip crosses the taker's threshold. We're done.
647 if (taker.reject(offer.quality()))
648 return true;
649
650 // This offer at the tip is not from the taker. We're done.
651 if (offer.owner() != taker.account())
652 return true;
653 }
654
655 // We ran out of offers. Can't advance.
656 return false;
657}
658
659// Fill as much of the offer as possible by consuming offers
660// already on the books. Return the status and the amount of
661// the offer to left unfilled.
664 Sandbox& sb,
665 Sandbox& sbCancel,
666 Amounts const& takerAmount)
667{
668 NetClock::time_point const when = sb.parentCloseTime();
669
670 beast::WrappedSink takerSink(j_, "Taker ");
671
672 Taker taker(
674 sb,
675 account_,
676 takerAmount,
677 ctx_.tx.getFlags(),
678 beast::Journal(takerSink));
679
680 // If the taker is unfunded before we begin crossing
681 // there's nothing to do - just return an error.
682 //
683 // We check this in preclaim, but when selling XRP
684 // charged fees can cause a user's available balance
685 // to go to 0 (by causing it to dip below the reserve)
686 // so we check this case again.
687 if (taker.unfunded())
688 {
689 JLOG(j_.debug()) << "Not crossing: taker is unfunded.";
690 return {tecUNFUNDED_OFFER, takerAmount};
691 }
692
693 try
694 {
696 return bridged_cross(taker, sb, sbCancel, when);
697
698 return direct_cross(taker, sb, sbCancel, when);
699 }
700 catch (std::exception const& e)
701 {
702 JLOG(j_.error()) << "Exception during offer crossing: " << e.what();
703 return {tecINTERNAL, taker.remaining_offer()};
704 }
705}
706
709 PaymentSandbox& psb,
710 PaymentSandbox& psbCancel,
711 Amounts const& takerAmount)
712{
713 try
714 {
715 // If the taker is unfunded before we begin crossing there's nothing
716 // to do - just return an error.
717 //
718 // We check this in preclaim, but when selling XRP charged fees can
719 // cause a user's available balance to go to 0 (by causing it to dip
720 // below the reserve) so we check this case again.
721 STAmount const inStartBalance =
722 accountFunds(psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);
723 if (inStartBalance <= beast::zero)
724 {
725 // The account balance can't cover even part of the offer.
726 JLOG(j_.debug()) << "Not crossing: taker is unfunded.";
727 return {tecUNFUNDED_OFFER, takerAmount};
728 }
729
730 // If the gateway has a transfer rate, accommodate that. The
731 // gateway takes its cut without any special consent from the
732 // offer taker. Set sendMax to allow for the gateway's cut.
733 Rate gatewayXferRate{QUALITY_ONE};
734 STAmount sendMax = takerAmount.in;
735 if (!sendMax.native() && (account_ != sendMax.getIssuer()))
736 {
737 gatewayXferRate = transferRate(psb, sendMax.getIssuer());
738 if (gatewayXferRate.value != QUALITY_ONE)
739 {
740 sendMax = multiplyRound(
741 takerAmount.in,
742 gatewayXferRate,
743 takerAmount.in.issue(),
744 true);
745 }
746 }
747
748 // Payment flow code compares quality after the transfer rate is
749 // included. Since transfer rate is incorporated compute threshold.
750 Quality threshold{takerAmount.out, sendMax};
751
752 // If we're creating a passive offer adjust the threshold so we only
753 // cross offers that have a better quality than this one.
754 std::uint32_t const txFlags = ctx_.tx.getFlags();
755 if (txFlags & tfPassive)
756 ++threshold;
757
758 // Don't send more than our balance.
759 if (sendMax > inStartBalance)
760 sendMax = inStartBalance;
761
762 // Always invoke flow() with the default path. However if neither
763 // of the takerAmount currencies are XRP then we cross through an
764 // additional path with XRP as the intermediate between two books.
765 // This second path we have to build ourselves.
766 STPathSet paths;
767 if (!takerAmount.in.native() && !takerAmount.out.native())
768 {
769 STPath path;
770 path.emplace_back(std::nullopt, xrpCurrency(), std::nullopt);
771 paths.emplace_back(std::move(path));
772 }
773 // Special handling for the tfSell flag.
774 STAmount deliver = takerAmount.out;
775 OfferCrossing offerCrossing = OfferCrossing::yes;
776 if (txFlags & tfSell)
777 {
778 offerCrossing = OfferCrossing::sell;
779 // We are selling, so we will accept *more* than the offer
780 // specified. Since we don't know how much they might offer,
781 // we allow delivery of the largest possible amount.
782 if (deliver.native())
784 else
785 // We can't use the maximum possible currency here because
786 // there might be a gateway transfer rate to account for.
787 // Since the transfer rate cannot exceed 200%, we use 1/2
788 // maxValue for our limit.
789 deliver = STAmount{
790 takerAmount.out.issue(),
793 }
794
795 // Call the payment engine's flow() to do the actual work.
796 auto const result = flow(
797 psb,
798 deliver,
799 account_,
800 account_,
801 paths,
802 true, // default path
803 !(txFlags & tfFillOrKill), // partial payment
804 true, // owner pays transfer fee
805 offerCrossing,
806 threshold,
807 sendMax,
808 j_);
809
810 // If stale offers were found remove them.
811 for (auto const& toRemove : result.removableOffers)
812 {
813 if (auto otr = psb.peek(keylet::offer(toRemove)))
814 offerDelete(psb, otr, j_);
815 if (auto otr = psbCancel.peek(keylet::offer(toRemove)))
816 offerDelete(psbCancel, otr, j_);
817 }
818
819 // Determine the size of the final offer after crossing.
820 auto afterCross = takerAmount; // If !tesSUCCESS offer unchanged
821 if (isTesSuccess(result.result()))
822 {
823 STAmount const takerInBalance = accountFunds(
824 psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);
825
826 if (takerInBalance <= beast::zero)
827 {
828 // If offer crossing exhausted the account's funds don't
829 // create the offer.
830 afterCross.in.clear();
831 afterCross.out.clear();
832 }
833 else
834 {
835 STAmount const rate{
836 Quality{takerAmount.out, takerAmount.in}.rate()};
837
838 if (txFlags & tfSell)
839 {
840 // If selling then scale the new out amount based on how
841 // much we sold during crossing. This preserves the offer
842 // Quality,
843
844 // Reduce the offer that is placed by the crossed amount.
845 // Note that we must ignore the portion of the
846 // actualAmountIn that may have been consumed by a
847 // gateway's transfer rate.
848 STAmount nonGatewayAmountIn = result.actualAmountIn;
849 if (gatewayXferRate.value != QUALITY_ONE)
850 nonGatewayAmountIn = divideRound(
851 result.actualAmountIn,
852 gatewayXferRate,
853 takerAmount.in.issue(),
854 true);
855
856 afterCross.in -= nonGatewayAmountIn;
857
858 // It's possible that the divRound will cause our subtract
859 // to go slightly negative. So limit afterCross.in to zero.
860 if (afterCross.in < beast::zero)
861 // We should verify that the difference *is* small, but
862 // what is a good threshold to check?
863 afterCross.in.clear();
864
865 afterCross.out = [&]() {
866 // Careful analysis showed that rounding up this
867 // divRound result could lead to placing a reduced
868 // offer in the ledger that blocks order books. So
869 // the fixReducedOffersV1 amendment changes the
870 // behavior to round down instead.
871 if (psb.rules().enabled(fixReducedOffersV1))
872 return divRoundStrict(
873 afterCross.in,
874 rate,
875 takerAmount.out.issue(),
876 false);
877
878 return divRound(
879 afterCross.in, rate, takerAmount.out.issue(), true);
880 }();
881 }
882 else
883 {
884 // If not selling, we scale the input based on the
885 // remaining output. This too preserves the offer
886 // Quality.
887 afterCross.out -= result.actualAmountOut;
888 XRPL_ASSERT(
889 afterCross.out >= beast::zero,
890 "ripple::CreateOffer::flowCross : minimum offer");
891 if (afterCross.out < beast::zero)
892 afterCross.out.clear();
893 afterCross.in = mulRound(
894 afterCross.out, rate, takerAmount.in.issue(), true);
895 }
896 }
897 }
898
899 // Return how much of the offer is left.
900 return {tesSUCCESS, afterCross};
901 }
902 catch (std::exception const& e)
903 {
904 JLOG(j_.error()) << "Exception during offer crossing: " << e.what();
905 }
906 return {tecINTERNAL, takerAmount};
907}
908
910CreateOffer::cross(Sandbox& sb, Sandbox& sbCancel, Amounts const& takerAmount)
911{
912 if (sb.rules().enabled(featureFlowCross))
913 {
914 PaymentSandbox psbFlow{&sb};
915 PaymentSandbox psbCancelFlow{&sbCancel};
916 auto const ret = flowCross(psbFlow, psbCancelFlow, takerAmount);
917 psbFlow.apply(sb);
918 psbCancelFlow.apply(sbCancel);
919 return ret;
920 }
921
922 Sandbox sbTaker{&sb};
923 Sandbox sbCancelTaker{&sbCancel};
924 auto const ret = takerCross(sbTaker, sbCancelTaker, takerAmount);
925 sbTaker.apply(sb);
926 sbCancelTaker.apply(sbCancel);
927 return ret;
928}
929
932{
933 std::string txt = amount.getText();
934 txt += "/";
935 txt += to_string(amount.issue().currency);
936 return txt;
937}
938
939void
941{
943 bool const pays_xrp = ctx_.tx.getFieldAmount(sfTakerPays).native();
944 bool const gets_xrp = ctx_.tx.getFieldAmount(sfTakerGets).native();
945 if (pays_xrp && !gets_xrp)
947 else if (gets_xrp && !pays_xrp)
949
950 return Transactor::preCompute();
951}
952
955{
956 using beast::zero;
957
958 std::uint32_t const uTxFlags = ctx_.tx.getFlags();
959
960 bool const bPassive(uTxFlags & tfPassive);
961 bool const bImmediateOrCancel(uTxFlags & tfImmediateOrCancel);
962 bool const bFillOrKill(uTxFlags & tfFillOrKill);
963 bool const bSell(uTxFlags & tfSell);
964
965 auto saTakerPays = ctx_.tx[sfTakerPays];
966 auto saTakerGets = ctx_.tx[sfTakerGets];
967
968 auto const cancelSequence = ctx_.tx[~sfOfferSequence];
969
970 // Note that we we use the value from the sequence or ticket as the
971 // offer sequence. For more explanation see comments in SeqProxy.h.
972 auto const offerSequence = ctx_.tx.getSeqProxy().value();
973
974 // This is the original rate of the offer, and is the rate at which
975 // it will be placed, even if crossing offers change the amounts that
976 // end up on the books.
977 auto uRate = getRate(saTakerGets, saTakerPays);
978
979 auto viewJ = ctx_.app.journal("View");
980
981 TER result = tesSUCCESS;
982
983 // Process a cancellation request that's passed along with an offer.
984 if (cancelSequence)
985 {
986 auto const sleCancel =
987 sb.peek(keylet::offer(account_, *cancelSequence));
988
989 // It's not an error to not find the offer to cancel: it might have
990 // been consumed or removed. If it is found, however, it's an error
991 // to fail to delete it.
992 if (sleCancel)
993 {
994 JLOG(j_.debug()) << "Create cancels order " << *cancelSequence;
995 result = offerDelete(sb, sleCancel, viewJ);
996 }
997 }
998
999 auto const expiration = ctx_.tx[~sfExpiration];
1000
1001 if (hasExpired(sb, expiration))
1002 {
1003 // If the offer has expired, the transaction has successfully
1004 // done nothing, so short circuit from here.
1005 //
1006 // The return code change is attached to featureDepositPreauth as a
1007 // convenience. The change is not big enough to deserve a fix code.
1008 TER const ter{
1009 sb.rules().enabled(featureDepositPreauth) ? TER{tecEXPIRED}
1010 : TER{tesSUCCESS}};
1011 return {ter, true};
1012 }
1013
1014 bool const bOpenLedger = sb.open();
1015 bool crossed = false;
1016
1017 if (result == tesSUCCESS)
1018 {
1019 // If a tick size applies, round the offer to the tick size
1020 auto const& uPaysIssuerID = saTakerPays.getIssuer();
1021 auto const& uGetsIssuerID = saTakerGets.getIssuer();
1022
1023 std::uint8_t uTickSize = Quality::maxTickSize;
1024 if (!isXRP(uPaysIssuerID))
1025 {
1026 auto const sle = sb.read(keylet::account(uPaysIssuerID));
1027 if (sle && sle->isFieldPresent(sfTickSize))
1028 uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
1029 }
1030 if (!isXRP(uGetsIssuerID))
1031 {
1032 auto const sle = sb.read(keylet::account(uGetsIssuerID));
1033 if (sle && sle->isFieldPresent(sfTickSize))
1034 uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
1035 }
1036 if (uTickSize < Quality::maxTickSize)
1037 {
1038 auto const rate =
1039 Quality{saTakerGets, saTakerPays}.round(uTickSize).rate();
1040
1041 // We round the side that's not exact,
1042 // just as if the offer happened to execute
1043 // at a slightly better (for the placer) rate
1044 if (bSell)
1045 {
1046 // this is a sell, round taker pays
1047 saTakerPays = multiply(saTakerGets, rate, saTakerPays.issue());
1048 }
1049 else
1050 {
1051 // this is a buy, round taker gets
1052 saTakerGets = divide(saTakerPays, rate, saTakerGets.issue());
1053 }
1054 if (!saTakerGets || !saTakerPays)
1055 {
1056 JLOG(j_.debug()) << "Offer rounded to zero";
1057 return {result, true};
1058 }
1059
1060 uRate = getRate(saTakerGets, saTakerPays);
1061 }
1062
1063 // We reverse pays and gets because during crossing we are taking.
1064 Amounts const takerAmount(saTakerGets, saTakerPays);
1065
1066 // The amount of the offer that is unfilled after crossing has been
1067 // performed. It may be equal to the original amount (didn't cross),
1068 // empty (fully crossed), or something in-between.
1069 Amounts place_offer;
1070
1071 JLOG(j_.debug()) << "Attempting cross: "
1072 << to_string(takerAmount.in.issue()) << " -> "
1073 << to_string(takerAmount.out.issue());
1074
1075 if (auto stream = j_.trace())
1076 {
1077 stream << " mode: " << (bPassive ? "passive " : "")
1078 << (bSell ? "sell" : "buy");
1079 stream << " in: " << format_amount(takerAmount.in);
1080 stream << " out: " << format_amount(takerAmount.out);
1081 }
1082
1083 std::tie(result, place_offer) = cross(sb, sbCancel, takerAmount);
1084
1085 // We expect the implementation of cross to succeed
1086 // or give a tec.
1087 XRPL_ASSERT(
1088 result == tesSUCCESS || isTecClaim(result),
1089 "ripple::CreateOffer::applyGuts : result is tesSUCCESS or "
1090 "tecCLAIM");
1091
1092 if (auto stream = j_.trace())
1093 {
1094 stream << "Cross result: " << transToken(result);
1095 stream << " in: " << format_amount(place_offer.in);
1096 stream << " out: " << format_amount(place_offer.out);
1097 }
1098
1099 if (result == tecFAILED_PROCESSING && bOpenLedger)
1100 result = telFAILED_PROCESSING;
1101
1102 if (result != tesSUCCESS)
1103 {
1104 JLOG(j_.debug()) << "final result: " << transToken(result);
1105 return {result, true};
1106 }
1107
1108 XRPL_ASSERT(
1109 saTakerGets.issue() == place_offer.in.issue(),
1110 "ripple::CreateOffer::applyGuts : taker gets issue match");
1111 XRPL_ASSERT(
1112 saTakerPays.issue() == place_offer.out.issue(),
1113 "ripple::CreateOffer::applyGuts : taker pays issue match");
1114
1115 if (takerAmount != place_offer)
1116 crossed = true;
1117
1118 // The offer that we need to place after offer crossing should
1119 // never be negative. If it is, something went very very wrong.
1120 if (place_offer.in < zero || place_offer.out < zero)
1121 {
1122 JLOG(j_.fatal()) << "Cross left offer negative!"
1123 << " in: " << format_amount(place_offer.in)
1124 << " out: " << format_amount(place_offer.out);
1125 return {tefINTERNAL, true};
1126 }
1127
1128 if (place_offer.in == zero || place_offer.out == zero)
1129 {
1130 JLOG(j_.debug()) << "Offer fully crossed!";
1131 return {result, true};
1132 }
1133
1134 // We now need to adjust the offer to reflect the amount left after
1135 // crossing. We reverse in and out here, since during crossing we
1136 // were the taker.
1137 saTakerPays = place_offer.out;
1138 saTakerGets = place_offer.in;
1139 }
1140
1141 XRPL_ASSERT(
1142 saTakerPays > zero && saTakerGets > zero,
1143 "ripple::CreateOffer::applyGuts : taker pays and gets positive");
1144
1145 if (result != tesSUCCESS)
1146 {
1147 JLOG(j_.debug()) << "final result: " << transToken(result);
1148 return {result, true};
1149 }
1150
1151 if (auto stream = j_.trace())
1152 {
1153 stream << "Place" << (crossed ? " remaining " : " ") << "offer:";
1154 stream << " Pays: " << saTakerPays.getFullText();
1155 stream << " Gets: " << saTakerGets.getFullText();
1156 }
1157
1158 // For 'fill or kill' offers, failure to fully cross means that the
1159 // entire operation should be aborted, with only fees paid.
1160 if (bFillOrKill)
1161 {
1162 JLOG(j_.trace()) << "Fill or Kill: offer killed";
1163 if (sb.rules().enabled(fix1578))
1164 return {tecKILLED, false};
1165 return {tesSUCCESS, false};
1166 }
1167
1168 // For 'immediate or cancel' offers, the amount remaining doesn't get
1169 // placed - it gets canceled and the operation succeeds.
1170 if (bImmediateOrCancel)
1171 {
1172 JLOG(j_.trace()) << "Immediate or cancel: offer canceled";
1173 if (!crossed && sb.rules().enabled(featureImmediateOfferKilled))
1174 // If the ImmediateOfferKilled amendment is enabled, any
1175 // ImmediateOrCancel offer that transfers absolutely no funds
1176 // returns tecKILLED rather than tesSUCCESS. Motivation for the
1177 // change is here: https://github.com/ripple/rippled/issues/4115
1178 return {tecKILLED, false};
1179 return {tesSUCCESS, true};
1180 }
1181
1182 auto const sleCreator = sb.peek(keylet::account(account_));
1183 if (!sleCreator)
1184 return {tefINTERNAL, false};
1185
1186 {
1187 XRPAmount reserve =
1188 sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1);
1189
1190 if (mPriorBalance < reserve)
1191 {
1192 // If we are here, the signing account had an insufficient reserve
1193 // *prior* to our processing. If something actually crossed, then
1194 // we allow this; otherwise, we just claim a fee.
1195 if (!crossed)
1196 result = tecINSUF_RESERVE_OFFER;
1197
1198 if (result != tesSUCCESS)
1199 {
1200 JLOG(j_.debug()) << "final result: " << transToken(result);
1201 }
1202
1203 return {result, true};
1204 }
1205 }
1206
1207 // We need to place the remainder of the offer into its order book.
1208 auto const offer_index = keylet::offer(account_, offerSequence);
1209
1210 // Add offer to owner's directory.
1211 auto const ownerNode = sb.dirInsert(
1213
1214 if (!ownerNode)
1215 {
1216 JLOG(j_.debug())
1217 << "final result: failed to add offer to owner's directory";
1218 return {tecDIR_FULL, true};
1219 }
1220
1221 // Update owner count.
1222 adjustOwnerCount(sb, sleCreator, 1, viewJ);
1223
1224 JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue())
1225 << " : " << to_string(saTakerGets.issue());
1226
1227 Book const book{saTakerPays.issue(), saTakerGets.issue()};
1228
1229 // Add offer to order book, using the original rate
1230 // before any crossing occured.
1231 auto dir = keylet::quality(keylet::book(book), uRate);
1232 bool const bookExisted = static_cast<bool>(sb.peek(dir));
1233
1234 auto const bookNode = sb.dirAppend(dir, offer_index, [&](SLE::ref sle) {
1235 sle->setFieldH160(sfTakerPaysCurrency, saTakerPays.issue().currency);
1236 sle->setFieldH160(sfTakerPaysIssuer, saTakerPays.issue().account);
1237 sle->setFieldH160(sfTakerGetsCurrency, saTakerGets.issue().currency);
1238 sle->setFieldH160(sfTakerGetsIssuer, saTakerGets.issue().account);
1239 sle->setFieldU64(sfExchangeRate, uRate);
1240 });
1241
1242 if (!bookNode)
1243 {
1244 JLOG(j_.debug()) << "final result: failed to add offer to book";
1245 return {tecDIR_FULL, true};
1246 }
1247
1248 auto sleOffer = std::make_shared<SLE>(offer_index);
1249 sleOffer->setAccountID(sfAccount, account_);
1250 sleOffer->setFieldU32(sfSequence, offerSequence);
1251 sleOffer->setFieldH256(sfBookDirectory, dir.key);
1252 sleOffer->setFieldAmount(sfTakerPays, saTakerPays);
1253 sleOffer->setFieldAmount(sfTakerGets, saTakerGets);
1254 sleOffer->setFieldU64(sfOwnerNode, *ownerNode);
1255 sleOffer->setFieldU64(sfBookNode, *bookNode);
1256 if (expiration)
1257 sleOffer->setFieldU32(sfExpiration, *expiration);
1258 if (bPassive)
1259 sleOffer->setFlag(lsfPassive);
1260 if (bSell)
1261 sleOffer->setFlag(lsfSell);
1262 sb.insert(sleOffer);
1263
1264 if (!bookExisted)
1266
1267 JLOG(j_.debug()) << "final result: success";
1268
1269 return {tesSUCCESS, true};
1270}
1271
1272TER
1274{
1275 // This is the ledger view that we work against. Transactions are applied
1276 // as we go on processing transactions.
1277 Sandbox sb(&ctx_.view());
1278
1279 // This is a ledger with just the fees paid and any unfunded or expired
1280 // offers we encounter removed. It's used when handling Fill-or-Kill offers,
1281 // if the order isn't going to be placed, to avoid wasting the work we did.
1282 Sandbox sbCancel(&ctx_.view());
1283
1284 auto const result = applyGuts(sb, sbCancel);
1285 if (result.second)
1286 sb.apply(ctx_.rawView());
1287 else
1288 sbCancel.apply(ctx_.rawView());
1289 return result.first;
1290}
1291
1292} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:59
Stream fatal() const
Definition: Journal.h:351
Stream error() const
Definition: Journal.h:345
Stream debug() const
Definition: Journal.h:327
Stream trace() const
Severity stream access functions.
Definition: Journal.h:321
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:442
@ 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:650
@ lsfHighDeepFreeze
@ lsfRequireAuth
@ lsfLowDeepFreeze
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:925
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:1092
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)