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