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