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