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