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  if (txFlags & tfSell)
740  {
741  // We are selling, so we will accept *more* than the offer
742  // specified. Since we don't know how much they might offer,
743  // we allow delivery of the largest possible amount.
744  if (deliver.native())
745  deliver = STAmount{STAmount::cMaxNative};
746  else
747  // We can't use the maximum possible currency here because
748  // there might be a gateway transfer rate to account for.
749  // Since the transfer rate cannot exceed 200%, we use 1/2
750  // maxValue for our limit.
751  deliver = STAmount{
752  takerAmount.out.issue(),
755  }
756 
757  // Call the payment engine's flow() to do the actual work.
758  auto const result = flow(
759  psb,
760  deliver,
761  account_,
762  account_,
763  paths,
764  true, // default path
765  !(txFlags & tfFillOrKill), // partial payment
766  true, // owner pays transfer fee
767  true, // offer crossing
768  threshold,
769  sendMax,
770  j_);
771 
772  // If stale offers were found remove them.
773  for (auto const& toRemove : result.removableOffers)
774  {
775  if (auto otr = psb.peek(keylet::offer(toRemove)))
776  offerDelete(psb, otr, j_);
777  if (auto otr = psbCancel.peek(keylet::offer(toRemove)))
778  offerDelete(psbCancel, otr, j_);
779  }
780 
781  // Determine the size of the final offer after crossing.
782  auto afterCross = takerAmount; // If !tesSUCCESS offer unchanged
783  if (isTesSuccess(result.result()))
784  {
785  STAmount const takerInBalance = accountFunds(
786  psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);
787 
788  if (takerInBalance <= beast::zero)
789  {
790  // If offer crossing exhausted the account's funds don't
791  // create the offer.
792  afterCross.in.clear();
793  afterCross.out.clear();
794  }
795  else
796  {
797  STAmount const rate{
798  Quality{takerAmount.out, takerAmount.in}.rate()};
799 
800  if (txFlags & tfSell)
801  {
802  // If selling then scale the new out amount based on how
803  // much we sold during crossing. This preserves the offer
804  // Quality,
805 
806  // Reduce the offer that is placed by the crossed amount.
807  // Note that we must ignore the portion of the
808  // actualAmountIn that may have been consumed by a
809  // gateway's transfer rate.
810  STAmount nonGatewayAmountIn = result.actualAmountIn;
811  if (gatewayXferRate.value != QUALITY_ONE)
812  nonGatewayAmountIn = divideRound(
813  result.actualAmountIn,
814  gatewayXferRate,
815  takerAmount.in.issue(),
816  true);
817 
818  afterCross.in -= nonGatewayAmountIn;
819 
820  // It's possible that the divRound will cause our subtract
821  // to go slightly negative. So limit afterCross.in to zero.
822  if (afterCross.in < beast::zero)
823  // We should verify that the difference *is* small, but
824  // what is a good threshold to check?
825  afterCross.in.clear();
826 
827  afterCross.out = divRound(
828  afterCross.in, rate, takerAmount.out.issue(), true);
829  }
830  else
831  {
832  // If not selling, we scale the input based on the
833  // remaining output. This too preserves the offer
834  // Quality.
835  afterCross.out -= result.actualAmountOut;
836  assert(afterCross.out >= beast::zero);
837  if (afterCross.out < beast::zero)
838  afterCross.out.clear();
839  afterCross.in = mulRound(
840  afterCross.out, rate, takerAmount.in.issue(), true);
841  }
842  }
843  }
844 
845  // Return how much of the offer is left.
846  return {tesSUCCESS, afterCross};
847  }
848  catch (std::exception const& e)
849  {
850  JLOG(j_.error()) << "Exception during offer crossing: " << e.what();
851  }
852  return {tecINTERNAL, takerAmount};
853 }
854 
856 CreateOffer::cross(Sandbox& sb, Sandbox& sbCancel, Amounts const& takerAmount)
857 {
858  if (sb.rules().enabled(featureFlowCross))
859  {
860  PaymentSandbox psbFlow{&sb};
861  PaymentSandbox psbCancelFlow{&sbCancel};
862  auto const ret = flowCross(psbFlow, psbCancelFlow, takerAmount);
863  psbFlow.apply(sb);
864  psbCancelFlow.apply(sbCancel);
865  return ret;
866  }
867 
868  Sandbox sbTaker{&sb};
869  Sandbox sbCancelTaker{&sbCancel};
870  auto const ret = takerCross(sbTaker, sbCancelTaker, takerAmount);
871  sbTaker.apply(sb);
872  sbCancelTaker.apply(sbCancel);
873  return ret;
874 }
875 
878 {
879  std::string txt = amount.getText();
880  txt += "/";
881  txt += to_string(amount.issue().currency);
882  return txt;
883 }
884 
885 void
887 {
889  bool const pays_xrp = ctx_.tx.getFieldAmount(sfTakerPays).native();
890  bool const gets_xrp = ctx_.tx.getFieldAmount(sfTakerGets).native();
891  if (pays_xrp && !gets_xrp)
893  else if (gets_xrp && !pays_xrp)
895 
896  return Transactor::preCompute();
897 }
898 
901 {
902  using beast::zero;
903 
904  std::uint32_t const uTxFlags = ctx_.tx.getFlags();
905 
906  bool const bPassive(uTxFlags & tfPassive);
907  bool const bImmediateOrCancel(uTxFlags & tfImmediateOrCancel);
908  bool const bFillOrKill(uTxFlags & tfFillOrKill);
909  bool const bSell(uTxFlags & tfSell);
910 
911  auto saTakerPays = ctx_.tx[sfTakerPays];
912  auto saTakerGets = ctx_.tx[sfTakerGets];
913 
914  auto const cancelSequence = ctx_.tx[~sfOfferSequence];
915 
916  // Note that we we use the value from the sequence or ticket as the
917  // offer sequence. For more explanation see comments in SeqProxy.h.
918  auto const offerSequence = ctx_.tx.getSeqProxy().value();
919 
920  // This is the original rate of the offer, and is the rate at which
921  // it will be placed, even if crossing offers change the amounts that
922  // end up on the books.
923  auto uRate = getRate(saTakerGets, saTakerPays);
924 
925  auto viewJ = ctx_.app.journal("View");
926 
927  TER result = tesSUCCESS;
928 
929  // Process a cancellation request that's passed along with an offer.
930  if (cancelSequence)
931  {
932  auto const sleCancel =
933  sb.peek(keylet::offer(account_, *cancelSequence));
934 
935  // It's not an error to not find the offer to cancel: it might have
936  // been consumed or removed. If it is found, however, it's an error
937  // to fail to delete it.
938  if (sleCancel)
939  {
940  JLOG(j_.debug()) << "Create cancels order " << *cancelSequence;
941  result = offerDelete(sb, sleCancel, viewJ);
942  }
943  }
944 
945  auto const expiration = ctx_.tx[~sfExpiration];
946 
947  if (hasExpired(sb, expiration))
948  {
949  // If the offer has expired, the transaction has successfully
950  // done nothing, so short circuit from here.
951  //
952  // The return code change is attached to featureDepositPreauth as a
953  // convenience. The change is not big enough to deserve a fix code.
954  TER const ter{
956  : TER{tesSUCCESS}};
957  return {ter, true};
958  }
959 
960  bool const bOpenLedger = sb.open();
961  bool crossed = false;
962 
963  if (result == tesSUCCESS)
964  {
965  // If a tick size applies, round the offer to the tick size
966  auto const& uPaysIssuerID = saTakerPays.getIssuer();
967  auto const& uGetsIssuerID = saTakerGets.getIssuer();
968 
969  std::uint8_t uTickSize = Quality::maxTickSize;
970  if (!isXRP(uPaysIssuerID))
971  {
972  auto const sle = sb.read(keylet::account(uPaysIssuerID));
973  if (sle && sle->isFieldPresent(sfTickSize))
974  uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
975  }
976  if (!isXRP(uGetsIssuerID))
977  {
978  auto const sle = sb.read(keylet::account(uGetsIssuerID));
979  if (sle && sle->isFieldPresent(sfTickSize))
980  uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
981  }
982  if (uTickSize < Quality::maxTickSize)
983  {
984  auto const rate =
985  Quality{saTakerGets, saTakerPays}.round(uTickSize).rate();
986 
987  // We round the side that's not exact,
988  // just as if the offer happened to execute
989  // at a slightly better (for the placer) rate
990  if (bSell)
991  {
992  // this is a sell, round taker pays
993  saTakerPays = multiply(saTakerGets, rate, saTakerPays.issue());
994  }
995  else
996  {
997  // this is a buy, round taker gets
998  saTakerGets = divide(saTakerPays, rate, saTakerGets.issue());
999  }
1000  if (!saTakerGets || !saTakerPays)
1001  {
1002  JLOG(j_.debug()) << "Offer rounded to zero";
1003  return {result, true};
1004  }
1005 
1006  uRate = getRate(saTakerGets, saTakerPays);
1007  }
1008 
1009  // We reverse pays and gets because during crossing we are taking.
1010  Amounts const takerAmount(saTakerGets, saTakerPays);
1011 
1012  // The amount of the offer that is unfilled after crossing has been
1013  // performed. It may be equal to the original amount (didn't cross),
1014  // empty (fully crossed), or something in-between.
1015  Amounts place_offer;
1016 
1017  JLOG(j_.debug()) << "Attempting cross: "
1018  << to_string(takerAmount.in.issue()) << " -> "
1019  << to_string(takerAmount.out.issue());
1020 
1021  if (auto stream = j_.trace())
1022  {
1023  stream << " mode: " << (bPassive ? "passive " : "")
1024  << (bSell ? "sell" : "buy");
1025  stream << " in: " << format_amount(takerAmount.in);
1026  stream << " out: " << format_amount(takerAmount.out);
1027  }
1028 
1029  std::tie(result, place_offer) = cross(sb, sbCancel, takerAmount);
1030 
1031  // We expect the implementation of cross to succeed
1032  // or give a tec.
1033  assert(result == tesSUCCESS || isTecClaim(result));
1034 
1035  if (auto stream = j_.trace())
1036  {
1037  stream << "Cross result: " << transToken(result);
1038  stream << " in: " << format_amount(place_offer.in);
1039  stream << " out: " << format_amount(place_offer.out);
1040  }
1041 
1042  if (result == tecFAILED_PROCESSING && bOpenLedger)
1043  result = telFAILED_PROCESSING;
1044 
1045  if (result != tesSUCCESS)
1046  {
1047  JLOG(j_.debug()) << "final result: " << transToken(result);
1048  return {result, true};
1049  }
1050 
1051  assert(saTakerGets.issue() == place_offer.in.issue());
1052  assert(saTakerPays.issue() == place_offer.out.issue());
1053 
1054  if (takerAmount != place_offer)
1055  crossed = true;
1056 
1057  // The offer that we need to place after offer crossing should
1058  // never be negative. If it is, something went very very wrong.
1059  if (place_offer.in < zero || place_offer.out < zero)
1060  {
1061  JLOG(j_.fatal()) << "Cross left offer negative!"
1062  << " in: " << format_amount(place_offer.in)
1063  << " out: " << format_amount(place_offer.out);
1064  return {tefINTERNAL, true};
1065  }
1066 
1067  if (place_offer.in == zero || place_offer.out == zero)
1068  {
1069  JLOG(j_.debug()) << "Offer fully crossed!";
1070  return {result, true};
1071  }
1072 
1073  // We now need to adjust the offer to reflect the amount left after
1074  // crossing. We reverse in and out here, since during crossing we
1075  // were the taker.
1076  saTakerPays = place_offer.out;
1077  saTakerGets = place_offer.in;
1078  }
1079 
1080  assert(saTakerPays > zero && saTakerGets > zero);
1081 
1082  if (result != tesSUCCESS)
1083  {
1084  JLOG(j_.debug()) << "final result: " << transToken(result);
1085  return {result, true};
1086  }
1087 
1088  if (auto stream = j_.trace())
1089  {
1090  stream << "Place" << (crossed ? " remaining " : " ") << "offer:";
1091  stream << " Pays: " << saTakerPays.getFullText();
1092  stream << " Gets: " << saTakerGets.getFullText();
1093  }
1094 
1095  // For 'fill or kill' offers, failure to fully cross means that the
1096  // entire operation should be aborted, with only fees paid.
1097  if (bFillOrKill)
1098  {
1099  JLOG(j_.trace()) << "Fill or Kill: offer killed";
1100  if (sb.rules().enabled(fix1578))
1101  return {tecKILLED, false};
1102  return {tesSUCCESS, false};
1103  }
1104 
1105  // For 'immediate or cancel' offers, the amount remaining doesn't get
1106  // placed - it gets canceled and the operation succeeds.
1107  if (bImmediateOrCancel)
1108  {
1109  JLOG(j_.trace()) << "Immediate or cancel: offer canceled";
1110  if (!crossed && sb.rules().enabled(featureImmediateOfferKilled))
1111  // If the ImmediateOfferKilled amendment is enabled, any
1112  // ImmediateOrCancel offer that transfers absolutely no funds
1113  // returns tecKILLED rather than tesSUCCESS. Motivation for the
1114  // change is here: https://github.com/ripple/rippled/issues/4115
1115  return {tecKILLED, false};
1116  return {tesSUCCESS, true};
1117  }
1118 
1119  auto const sleCreator = sb.peek(keylet::account(account_));
1120  if (!sleCreator)
1121  return {tefINTERNAL, false};
1122 
1123  {
1124  XRPAmount reserve =
1125  sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1);
1126 
1127  if (mPriorBalance < reserve)
1128  {
1129  // If we are here, the signing account had an insufficient reserve
1130  // *prior* to our processing. If something actually crossed, then
1131  // we allow this; otherwise, we just claim a fee.
1132  if (!crossed)
1133  result = tecINSUF_RESERVE_OFFER;
1134 
1135  if (result != tesSUCCESS)
1136  {
1137  JLOG(j_.debug()) << "final result: " << transToken(result);
1138  }
1139 
1140  return {result, true};
1141  }
1142  }
1143 
1144  // We need to place the remainder of the offer into its order book.
1145  auto const offer_index = keylet::offer(account_, offerSequence);
1146 
1147  // Add offer to owner's directory.
1148  auto const ownerNode = sb.dirInsert(
1150 
1151  if (!ownerNode)
1152  {
1153  JLOG(j_.debug())
1154  << "final result: failed to add offer to owner's directory";
1155  return {tecDIR_FULL, true};
1156  }
1157 
1158  // Update owner count.
1159  adjustOwnerCount(sb, sleCreator, 1, viewJ);
1160 
1161  JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue())
1162  << " : " << to_string(saTakerGets.issue());
1163 
1164  Book const book{saTakerPays.issue(), saTakerGets.issue()};
1165 
1166  // Add offer to order book, using the original rate
1167  // before any crossing occured.
1168  auto dir = keylet::quality(keylet::book(book), uRate);
1169  bool const bookExisted = static_cast<bool>(sb.peek(dir));
1170 
1171  auto const bookNode = sb.dirAppend(dir, offer_index, [&](SLE::ref sle) {
1172  sle->setFieldH160(sfTakerPaysCurrency, saTakerPays.issue().currency);
1173  sle->setFieldH160(sfTakerPaysIssuer, saTakerPays.issue().account);
1174  sle->setFieldH160(sfTakerGetsCurrency, saTakerGets.issue().currency);
1175  sle->setFieldH160(sfTakerGetsIssuer, saTakerGets.issue().account);
1176  sle->setFieldU64(sfExchangeRate, uRate);
1177  });
1178 
1179  if (!bookNode)
1180  {
1181  JLOG(j_.debug()) << "final result: failed to add offer to book";
1182  return {tecDIR_FULL, true};
1183  }
1184 
1185  auto sleOffer = std::make_shared<SLE>(offer_index);
1186  sleOffer->setAccountID(sfAccount, account_);
1187  sleOffer->setFieldU32(sfSequence, offerSequence);
1188  sleOffer->setFieldH256(sfBookDirectory, dir.key);
1189  sleOffer->setFieldAmount(sfTakerPays, saTakerPays);
1190  sleOffer->setFieldAmount(sfTakerGets, saTakerGets);
1191  sleOffer->setFieldU64(sfOwnerNode, *ownerNode);
1192  sleOffer->setFieldU64(sfBookNode, *bookNode);
1193  if (expiration)
1194  sleOffer->setFieldU32(sfExpiration, *expiration);
1195  if (bPassive)
1196  sleOffer->setFlag(lsfPassive);
1197  if (bSell)
1198  sleOffer->setFlag(lsfSell);
1199  sb.insert(sleOffer);
1200 
1201  if (!bookExisted)
1203 
1204  JLOG(j_.debug()) << "final result: success";
1205 
1206  return {tesSUCCESS, true};
1207 }
1208 
1209 TER
1211 {
1212  // This is the ledger view that we work against. Transactions are applied
1213  // as we go on processing transactions.
1214  Sandbox sb(&ctx_.view());
1215 
1216  // This is a ledger with just the fees paid and any unfunded or expired
1217  // offers we encounter removed. It's used when handling Fill-or-Kill offers,
1218  // if the order isn't going to be placed, to avoid wasting the work we did.
1219  Sandbox sbCancel(&ctx_.view());
1220 
1221  auto const result = applyGuts(sb, sbCancel);
1222  if (result.second)
1223  sb.apply(ctx_.rawView());
1224  else
1225  sbCancel.apply(ctx_.rawView());
1226  return result.first;
1227 }
1228 
1229 } // 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:135
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
ripple::tecUNFUNDED_OFFER
@ tecUNFUNDED_OFFER
Definition: TER.h:251
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:303
ripple::sfOwnerCount
const SF_UINT32 sfOwnerCount
ripple::transferRate
Rate transferRate(ReadView const &view, AccountID const &issuer)
Definition: View.cpp:471
ripple::tecFROZEN
@ tecFROZEN
Definition: TER.h:270
ripple::Application::getOrderBookDB
virtual OrderBookDB & getOrderBookDB()=0
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:130
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:34
ripple::tefINTERNAL
@ tefINTERNAL
Definition: TER.h:155
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:93
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:76
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:88
ripple::PreclaimContext::j
const beast::Journal j
Definition: Transactor.h:60
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::composed_quality
Quality composed_quality(Quality const &lhs, Quality const &rhs)
Definition: Quality.cpp:101
ripple::STAmount::clear
void clear()
Definition: STAmount.h:409
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:347
ripple::Taker
Definition: Taker.h:240
ripple::terNO_LINE
@ terNO_LINE
Definition: TER.h:200
ripple::describeOwnerDir
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition: View.cpp:731
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:597
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:254
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::STAmount::getText
std::string getText() const override
Definition: STAmount.cpp:571
ripple::CrossType::XrpToIou
@ XrpToIou
ripple::STAmount::cMaxNative
static const std::uint64_t cMaxNative
Definition: STAmount.h:68
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:222
ripple::Issue::currency
Currency currency
Definition: Issue.h:37
ripple::tfOfferCreateMask
constexpr std::uint32_t tfOfferCreateMask
Definition: TxFlags.h:97
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:210
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:179
ripple::STTx::getSeqProxy
SeqProxy getSeqProxy() const
Definition: STTx.cpp:183
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:446
ripple::tfPassive
constexpr std::uint32_t tfPassive
Definition: TxFlags.h:93
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:186
ripple::temBAD_ISSUER
@ temBAD_ISSUER
Definition: TER.h:91
ripple::sfTakerGetsCurrency
const SF_UINT160 sfTakerGetsCurrency
ripple::CreateOffer::applyGuts
std::pair< TER, bool > applyGuts(Sandbox &view, Sandbox &view_cancel)
Definition: CreateOffer.cpp:900
ripple::PreflightContext::j
const beast::Journal j
Definition: Transactor.h:38
ripple::isTecClaim
bool isTecClaim(TER x)
Definition: TER.h:603
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:78
ripple::ApplyView
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:134
ripple::lsfHighAuth
@ lsfHighAuth
Definition: LedgerFormats.h:255
ripple::tecKILLED
@ tecKILLED
Definition: TER.h:283
ripple::mulRound
STAmount mulRound(STAmount const &v1, STAmount const &v2, Issue const &issue, bool roundUp)
Definition: STAmount.cpp:1305
ripple::ApplyContext::app
Application & app
Definition: ApplyContext.h:47
ripple::TOffer::quality
const Quality quality() const noexcept
Returns the quality of the offer.
Definition: Offer.h:75
ripple::STAmount::getIssuer
AccountID const & getIssuer() const
Definition: STAmount.h:359
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:495
ripple::divide
STAmount divide(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:86
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:109
ripple::isGlobalFrozen
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Definition: View.cpp:188
ripple::CreateOffer::format_amount
static std::string format_amount(STAmount const &amount)
Definition: CreateOffer.cpp:877
ripple::lsfRequireAuth
@ lsfRequireAuth
Definition: LedgerFormats.h:226
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:713
ripple::telFAILED_PROCESSING
@ telFAILED_PROCESSING
Definition: TER.h:55
ripple::flow
path::RippleCalc::Output flow(PaymentSandbox &view, STAmount const &deliver, AccountID const &src, AccountID const &dst, STPathSet const &paths, bool defaultPaths, bool partialPayment, bool ownerPaysTransferFee, bool offerCrossing, std::optional< Quality > const &limitQuality, std::optional< STAmount > const &sendMax, beast::Journal j, path::detail::FlowDebugInfo *flowDebugInfo=nullptr)
Make a payment from the src account to the dst account.
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:249
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:133
ripple::CreateOffer::preCompute
void preCompute() override
Gather information beyond what the Transactor base class gathers.
Definition: CreateOffer.cpp:886
ripple::offerDelete
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition: View.cpp:893
ripple::divRound
STAmount divRound(STAmount const &num, STAmount const &den, Issue const &issue, bool roundUp)
Definition: STAmount.cpp:1391
ripple::TERSubset
Definition: TER.h:340
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:253
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:102
ripple::fixTakerDryOfferRemoval
const uint256 fixTakerDryOfferRemoval
ripple::terNO_AUTH
@ terNO_AUTH
Definition: TER.h:199
ripple::STAmount
Definition: STAmount.h:45
beast::Journal::error
Stream error() const
Definition: Journal.h:333
std::chrono::time_point
ripple::tecINTERNAL
@ tecINTERNAL
Definition: TER.h:277
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:481
ripple::sfTakerGets
const SF_AMOUNT sfTakerGets
ripple::STTx
Definition: STTx.h:45
ripple::sfTickSize
const SF_UINT8 sfTickSize
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:89
ripple::temBAD_AMOUNT
@ temBAD_AMOUNT
Definition: TER.h:87
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:1210
ripple::keylet::book
static const book_t book
Definition: Indexes.h:101
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:193
ripple::CreateOffer::cross_type_
CrossType cross_type_
Definition: CreateOffer.h:139
ripple::temREDUNDANT
@ temREDUNDANT
Definition: TER.h:110
ripple::lsfPassive
@ lsfPassive
Definition: LedgerFormats.h:248
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:95
ripple::accountFunds
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition: View.cpp:267
ripple::tecDIR_FULL
@ tecDIR_FULL
Definition: TER.h:254
ripple::terNO_ACCOUNT
@ terNO_ACCOUNT
Definition: TER.h:198
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:329
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:125
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:96
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:856
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:268
ripple::tecEXPIRED
@ tecEXPIRED
Definition: TER.h:281
ripple::Fees::accountReserve
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
Definition: ReadView.h:66
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:266
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:95
ripple::fhIGNORE_FREEZE
@ fhIGNORE_FREEZE
Definition: View.h:76
ripple::Transactor::ctx_
ApplyContext & ctx_
Definition: Transactor.h:88
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
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:228
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:205
ripple::STAmount::cMaxOffset
static const int cMaxOffset
Definition: STAmount.h:63
ripple::tecNO_AUTH
@ tecNO_AUTH
Definition: TER.h:267
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:89
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:256
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:353
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:222
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:94
ripple::Transactor::preCompute
virtual void preCompute()
Definition: Transactor.cpp:437
ripple::STObject::getFieldAmount
STAmount const & getFieldAmount(SField const &field) const
Definition: STObject.cpp:603
ripple::xrpCurrency
Currency const & xrpCurrency()
XRP currency.
Definition: UintTypes.cpp:121
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:38
ripple::STAmount::cMaxValue
static const std::uint64_t cMaxValue
Definition: STAmount.h:67
std::exception::what
T what(T... args)
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::OrderBookDB::addOrderBook
void addOrderBook(Book const &)
Definition: OrderBookDB.cpp:152