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