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