rippled
Offer_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2017 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/protocol/Feature.h>
21 #include <ripple/protocol/Quality.h>
22 #include <ripple/protocol/jss.h>
23 #include <test/jtx.h>
24 #include <test/jtx/PathSet.h>
25 #include <test/jtx/WSClient.h>
26 
27 namespace ripple {
28 namespace test {
29 
30 class Offer_test : public beast::unit_test::suite
31 {
32  XRPAmount
34  {
35  return env.current()->fees().accountReserve(count);
36  }
37 
40  {
41  return env.current()->info().parentCloseTime.time_since_epoch().count();
42  }
43 
44  static auto
45  xrpMinusFee(jtx::Env const& env, std::int64_t xrpAmount)
47  {
48  using namespace jtx;
49  auto feeDrops = env.current()->fees().base;
50  return drops(dropsPerXRP * xrpAmount - feeDrops);
51  }
52 
53  static auto
55  jtx::Env& env,
56  jtx::Account const& acct_a,
57  jtx::Account const& acct_b,
58  std::string const& currency)
59  {
60  Json::Value jvParams;
61  jvParams[jss::ledger_index] = "current";
62  jvParams[jss::ripple_state][jss::currency] = currency;
63  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
64  jvParams[jss::ripple_state][jss::accounts].append(acct_a.human());
65  jvParams[jss::ripple_state][jss::accounts].append(acct_b.human());
66  return env.rpc(
67  "json", "ledger_entry", to_string(jvParams))[jss::result];
68  }
69 
70  static auto
72  {
73  Json::Value jvParams;
74  jvParams[jss::ledger_index] = "current";
75  jvParams[jss::account_root] = acct.human();
76  return env.rpc(
77  "json", "ledger_entry", to_string(jvParams))[jss::result];
78  }
79 
80  static auto
82  jtx::Env& env,
83  jtx::Account const& acct,
84  std::uint32_t offer_seq)
85  {
86  Json::Value jvParams;
87  jvParams[jss::offer][jss::account] = acct.human();
88  jvParams[jss::offer][jss::seq] = offer_seq;
89  return env.rpc(
90  "json", "ledger_entry", to_string(jvParams))[jss::result];
91  }
92 
93  static auto
95  jtx::Env& env,
96  Issue const& taker_pays,
97  Issue const& taker_gets)
98  {
99  Json::Value jvbp;
100  jvbp[jss::ledger_index] = "current";
101  jvbp[jss::taker_pays][jss::currency] = to_string(taker_pays.currency);
102  jvbp[jss::taker_pays][jss::issuer] = to_string(taker_pays.account);
103  jvbp[jss::taker_gets][jss::currency] = to_string(taker_gets.currency);
104  jvbp[jss::taker_gets][jss::issuer] = to_string(taker_gets.account);
105  return env.rpc("json", "book_offers", to_string(jvbp))[jss::result];
106  }
107 
108 public:
109  void
111  {
112  testcase("Incorrect Removal of Funded Offers");
113 
114  // We need at least two paths. One at good quality and one at bad
115  // quality. The bad quality path needs two offer books in a row.
116  // Each offer book should have two offers at the same quality, the
117  // offers should be completely consumed, and the payment should
118  // should require both offers to be satisfied. The first offer must
119  // be "taker gets" XRP. Old, broken would remove the first
120  // "taker gets" xrp offer, even though the offer is still funded and
121  // not used for the payment.
122 
123  using namespace jtx;
124  Env env{*this, features};
125 
126  auto const gw = Account{"gateway"};
127  auto const USD = gw["USD"];
128  auto const BTC = gw["BTC"];
129  Account const alice{"alice"};
130  Account const bob{"bob"};
131  Account const carol{"carol"};
132 
133  env.fund(XRP(10000), alice, bob, carol, gw);
134  env.trust(USD(1000), alice, bob, carol);
135  env.trust(BTC(1000), alice, bob, carol);
136 
137  env(pay(gw, alice, BTC(1000)));
138 
139  env(pay(gw, carol, USD(1000)));
140  env(pay(gw, carol, BTC(1000)));
141 
142  // Must be two offers at the same quality
143  // "taker gets" must be XRP
144  // (Different amounts so I can distinguish the offers)
145  env(offer(carol, BTC(49), XRP(49)));
146  env(offer(carol, BTC(51), XRP(51)));
147 
148  // Offers for the poor quality path
149  // Must be two offers at the same quality
150  env(offer(carol, XRP(50), USD(50)));
151  env(offer(carol, XRP(50), USD(50)));
152 
153  // Offers for the good quality path
154  env(offer(carol, BTC(1), USD(100)));
155 
156  PathSet paths(Path(XRP, USD), Path(USD));
157 
158  env(pay(alice, bob, USD(100)),
159  json(paths.json()),
160  sendmax(BTC(1000)),
162 
163  env.require(balance(bob, USD(100)));
164  BEAST_EXPECT(
165  !isOffer(env, carol, BTC(1), USD(100)) &&
166  isOffer(env, carol, BTC(49), XRP(49)));
167  }
168 
169  void
171  {
172  testcase("Removing Canceled Offers");
173 
174  using namespace jtx;
175  Env env{*this, features};
176 
177  auto const gw = Account{"gateway"};
178  auto const alice = Account{"alice"};
179  auto const USD = gw["USD"];
180 
181  env.fund(XRP(10000), alice, gw);
182  env.close();
183  env.trust(USD(100), alice);
184  env.close();
185 
186  env(pay(gw, alice, USD(50)));
187  env.close();
188 
189  auto const offer1Seq = env.seq(alice);
190 
191  env(offer(alice, XRP(500), USD(100)), require(offers(alice, 1)));
192  env.close();
193 
194  BEAST_EXPECT(isOffer(env, alice, XRP(500), USD(100)));
195 
196  // cancel the offer above and replace it with a new offer
197  auto const offer2Seq = env.seq(alice);
198 
199  env(offer(alice, XRP(300), USD(100)),
200  json(jss::OfferSequence, offer1Seq),
201  require(offers(alice, 1)));
202  env.close();
203 
204  BEAST_EXPECT(
205  isOffer(env, alice, XRP(300), USD(100)) &&
206  !isOffer(env, alice, XRP(500), USD(100)));
207 
208  // Test canceling non-existent offer.
209  // auto const offer3Seq = env.seq (alice);
210 
211  env(offer(alice, XRP(400), USD(200)),
212  json(jss::OfferSequence, offer1Seq),
213  require(offers(alice, 2)));
214  env.close();
215 
216  BEAST_EXPECT(
217  isOffer(env, alice, XRP(300), USD(100)) &&
218  isOffer(env, alice, XRP(400), USD(200)));
219 
220  // Test cancellation now with OfferCancel tx
221  auto const offer4Seq = env.seq(alice);
222  env(offer(alice, XRP(222), USD(111)), require(offers(alice, 3)));
223  env.close();
224 
225  BEAST_EXPECT(isOffer(env, alice, XRP(222), USD(111)));
226  env(offer_cancel(alice, offer4Seq));
227  env.close();
228  BEAST_EXPECT(env.seq(alice) == offer4Seq + 2);
229 
230  BEAST_EXPECT(!isOffer(env, alice, XRP(222), USD(111)));
231 
232  // Create an offer that both fails with a tecEXPIRED code and removes
233  // an offer. Show that the attempt to remove the offer fails.
234  env.require(offers(alice, 2));
235 
236  // featureDepositPreauths changes the return code on an expired Offer.
237  // Adapt to that.
238  bool const featPreauth{features[featureDepositPreauth]};
239  env(offer(alice, XRP(5), USD(2)),
241  json(jss::OfferSequence, offer2Seq),
242  ter(featPreauth ? TER{tecEXPIRED} : TER{tesSUCCESS}));
243  env.close();
244 
245  env.require(offers(alice, 2));
246  BEAST_EXPECT(isOffer(env, alice, XRP(300), USD(100))); // offer2
247  BEAST_EXPECT(!isOffer(env, alice, XRP(5), USD(2))); // expired
248  }
249 
250  void
252  {
253  testcase("Tiny payments");
254 
255  // Regression test for tiny payments
256  using namespace jtx;
257  using namespace std::chrono_literals;
258  auto const alice = Account{"alice"};
259  auto const bob = Account{"bob"};
260  auto const carol = Account{"carol"};
261  auto const gw = Account{"gw"};
262 
263  auto const USD = gw["USD"];
264  auto const EUR = gw["EUR"];
265 
266  Env env{*this, features};
267 
268  env.fund(XRP(10000), alice, bob, carol, gw);
269  env.trust(USD(1000), alice, bob, carol);
270  env.trust(EUR(1000), alice, bob, carol);
271  env(pay(gw, alice, USD(100)));
272  env(pay(gw, carol, EUR(100)));
273 
274  // Create more offers than the loop max count in DeliverNodeReverse
275  // Note: the DeliverNodeReverse code has been removed; however since
276  // this is a regression test the original test is being left as-is for
277  // now.
278  for (int i = 0; i < 101; ++i)
279  env(offer(carol, USD(1), EUR(2)));
280 
281  env(pay(alice, bob, EUR(epsilon)), path(~EUR), sendmax(USD(100)));
282  }
283 
284  void
286  {
287  testcase("XRP Tiny payments");
288 
289  // Regression test for tiny xrp payments
290  // In some cases, when the payment code calculates
291  // the amount of xrp needed as input to an xrp->iou offer
292  // it would incorrectly round the amount to zero (even when
293  // round-up was set to true).
294  // The bug would cause funded offers to be incorrectly removed
295  // because the code thought they were unfunded.
296  // The conditions to trigger the bug are:
297  // 1) When we calculate the amount of input xrp needed for an offer
298  // from xrp->iou, the amount is less than 1 drop (after rounding
299  // up the float representation).
300  // 2) There is another offer in the same book with a quality
301  // sufficiently bad that when calculating the input amount
302  // needed the amount is not set to zero.
303 
304  using namespace jtx;
305  using namespace std::chrono_literals;
306  auto const alice = Account{"alice"};
307  auto const bob = Account{"bob"};
308  auto const carol = Account{"carol"};
309  auto const dan = Account{"dan"};
310  auto const erin = Account{"erin"};
311  auto const gw = Account{"gw"};
312 
313  auto const USD = gw["USD"];
314  Env env{*this, features};
315 
316  env.fund(XRP(10000), alice, bob, carol, dan, erin, gw);
317  env.close();
318  env.trust(USD(1000), alice, bob, carol, dan, erin);
319  env.close();
320  env(pay(gw, carol, USD(0.99999)));
321  env(pay(gw, dan, USD(1)));
322  env(pay(gw, erin, USD(1)));
323  env.close();
324 
325  // Carol doesn't quite have enough funds for this offer
326  // The amount left after this offer is taken will cause
327  // STAmount to incorrectly round to zero when the next offer
328  // (at a good quality) is considered. (when the now removed
329  // stAmountCalcSwitchover2 patch was inactive)
330  env(offer(carol, drops(1), USD(0.99999)));
331  // Offer at a quality poor enough so when the input xrp is
332  // calculated in the reverse pass, the amount is not zero.
333  env(offer(dan, XRP(100), USD(1)));
334 
335  env.close();
336  // This is the funded offer that will be incorrectly removed.
337  // It is considered after the offer from carol, which leaves a
338  // tiny amount left to pay. When calculating the amount of xrp
339  // needed for this offer, it will incorrectly compute zero in both
340  // the forward and reverse passes (when the now removed
341  // stAmountCalcSwitchover2 was inactive.)
342  env(offer(erin, drops(2), USD(1)));
343 
344  env(pay(alice, bob, USD(1)),
345  path(~USD),
346  sendmax(XRP(102)),
348 
349  env.require(offers(carol, 0), offers(dan, 1));
350 
351  // offer was correctly consumed. There is still some
352  // liquidity left on that offer.
353  env.require(balance(erin, USD(0.99999)), offers(erin, 1));
354  }
355 
356  void
358  {
359  testcase("Rm small increased q offers XRP");
360 
361  // Carol places an offer, but cannot fully fund the offer. When her
362  // funding is taken into account, the offer's quality drops below its
363  // initial quality and has an input amount of 1 drop. This is removed as
364  // an offer that may block offer books.
365 
366  using namespace jtx;
367  using namespace std::chrono_literals;
368  auto const alice = Account{"alice"};
369  auto const bob = Account{"bob"};
370  auto const carol = Account{"carol"};
371  auto const gw = Account{"gw"};
372 
373  auto const USD = gw["USD"];
374 
375  // Test offer crossing
376  for (auto crossBothOffers : {false, true})
377  {
378  Env env{*this, features};
379 
380  env.fund(XRP(10000), alice, bob, carol, gw);
381  env.close();
382  env.trust(USD(1000), alice, bob, carol);
383  // underfund carol's offer
384  auto initialCarolUSD = USD(0.499);
385  env(pay(gw, carol, initialCarolUSD));
386  env(pay(gw, bob, USD(100)));
387  env.close();
388  // This offer is underfunded
389  env(offer(carol, drops(1), USD(1)));
390  env.close();
391  // offer at a lower quality
392  env(offer(bob, drops(2), USD(1), tfPassive));
393  env.close();
394  env.require(offers(bob, 1), offers(carol, 1));
395 
396  // alice places an offer that crosses carol's; depending on
397  // "crossBothOffers" it may cross bob's as well
398  auto aliceTakerGets = crossBothOffers ? drops(2) : drops(1);
399  env(offer(alice, USD(1), aliceTakerGets));
400  env.close();
401 
402  if (features[fixRmSmallIncreasedQOffers])
403  {
404  env.require(
405  offers(carol, 0),
406  balance(
407  carol,
408  initialCarolUSD)); // offer is removed but not taken
409  if (crossBothOffers)
410  {
411  env.require(
412  offers(alice, 0),
413  balance(alice, USD(1))); // alice's offer is crossed
414  }
415  else
416  {
417  env.require(
418  offers(alice, 1),
419  balance(
420  alice, USD(0))); // alice's offer is not crossed
421  }
422  }
423  else
424  {
425  env.require(
426  offers(alice, 1),
427  offers(bob, 1),
428  offers(carol, 1),
429  balance(alice, USD(0)),
430  balance(
431  carol,
432  initialCarolUSD)); // offer is not crossed at all
433  }
434  }
435 
436  // Test payments
437  for (auto partialPayment : {false, true})
438  {
439  Env env{*this, features};
440 
441  env.fund(XRP(10000), alice, bob, carol, gw);
442  env.close();
443  env.trust(USD(1000), alice, bob, carol);
444  env.close();
445  auto const initialCarolUSD = USD(0.999);
446  env(pay(gw, carol, initialCarolUSD));
447  env.close();
448  env(pay(gw, bob, USD(100)));
449  env.close();
450  env(offer(carol, drops(1), USD(1)));
451  env.close();
452  env(offer(bob, drops(2), USD(2), tfPassive));
453  env.close();
454  env.require(offers(bob, 1), offers(carol, 1));
455 
456  std::uint32_t const flags = partialPayment
459 
460  TER const expectedTer =
461  partialPayment ? TER{tesSUCCESS} : TER{tecPATH_PARTIAL};
462 
463  env(pay(alice, bob, USD(5)),
464  path(~USD),
465  sendmax(XRP(1)),
466  txflags(flags),
467  ter(expectedTer));
468  env.close();
469 
470  if (features[fixRmSmallIncreasedQOffers])
471  {
472  if (expectedTer == tesSUCCESS)
473  {
474  env.require(offers(carol, 0));
475  env.require(balance(
476  carol,
477  initialCarolUSD)); // offer is removed but not taken
478  }
479  else
480  {
481  // TODO: Offers are not removed when payments fail
482  // If that is addressed, the test should show that carol's
483  // offer is removed but not taken, as in the other branch of
484  // this if statement
485  }
486  }
487  else
488  {
489  if (partialPayment)
490  {
491  env.require(offers(carol, 0));
492  env.require(
493  balance(carol, USD(0))); // offer is removed and taken
494  }
495  else
496  {
497  // offer is not removed or taken
498  BEAST_EXPECT(isOffer(env, carol, drops(1), USD(1)));
499  }
500  }
501  }
502  }
503 
504  void
506  {
507  testcase("Rm small increased q offers IOU");
508 
509  // Carol places an offer, but cannot fully fund the offer. When her
510  // funding is taken into account, the offer's quality drops below its
511  // initial quality and has an input amount of 1 drop. This is removed as
512  // an offer that may block offer books.
513 
514  using namespace jtx;
515  using namespace std::chrono_literals;
516  auto const alice = Account{"alice"};
517  auto const bob = Account{"bob"};
518  auto const carol = Account{"carol"};
519  auto const gw = Account{"gw"};
520 
521  auto const USD = gw["USD"];
522  auto const EUR = gw["EUR"];
523 
524  auto tinyAmount = [&](IOU const& iou) -> PrettyAmount {
525  STAmount amt(
526  iou.issue(),
527  /*mantissa*/ 1,
528  /*exponent*/ -81);
529  return PrettyAmount(amt, iou.account.name());
530  };
531 
532  // Test offer crossing
533  for (auto crossBothOffers : {false, true})
534  {
535  Env env{*this, features};
536 
537  env.fund(XRP(10000), alice, bob, carol, gw);
538  env.close();
539  env.trust(USD(1000), alice, bob, carol);
540  env.trust(EUR(1000), alice, bob, carol);
541  // underfund carol's offer
542  auto initialCarolUSD = tinyAmount(USD);
543  env(pay(gw, carol, initialCarolUSD));
544  env(pay(gw, bob, USD(100)));
545  env(pay(gw, alice, EUR(100)));
546  env.close();
547  // This offer is underfunded
548  env(offer(carol, EUR(1), USD(10)));
549  env.close();
550  // offer at a lower quality
551  env(offer(bob, EUR(1), USD(5), tfPassive));
552  env.close();
553  env.require(offers(bob, 1), offers(carol, 1));
554 
555  // alice places an offer that crosses carol's; depending on
556  // "crossBothOffers" it may cross bob's as well
557  // Whatever
558  auto aliceTakerGets = crossBothOffers ? EUR(0.2) : EUR(0.1);
559  env(offer(alice, USD(1), aliceTakerGets));
560  env.close();
561 
562  if (features[fixRmSmallIncreasedQOffers])
563  {
564  env.require(
565  offers(carol, 0),
566  balance(
567  carol,
568  initialCarolUSD)); // offer is removed but not taken
569  if (crossBothOffers)
570  {
571  env.require(
572  offers(alice, 0),
573  balance(alice, USD(1))); // alice's offer is crossed
574  }
575  else
576  {
577  env.require(
578  offers(alice, 1),
579  balance(
580  alice, USD(0))); // alice's offer is not crossed
581  }
582  }
583  else
584  {
585  env.require(
586  offers(alice, 1),
587  offers(bob, 1),
588  offers(carol, 1),
589  balance(alice, USD(0)),
590  balance(
591  carol,
592  initialCarolUSD)); // offer is not crossed at all
593  }
594  }
595 
596  // Test payments
597  for (auto partialPayment : {false, true})
598  {
599  Env env{*this, features};
600 
601  env.fund(XRP(10000), alice, bob, carol, gw);
602  env.close();
603  env.trust(USD(1000), alice, bob, carol);
604  env.trust(EUR(1000), alice, bob, carol);
605  env.close();
606  // underfund carol's offer
607  auto const initialCarolUSD = tinyAmount(USD);
608  env(pay(gw, carol, initialCarolUSD));
609  env(pay(gw, bob, USD(100)));
610  env(pay(gw, alice, EUR(100)));
611  env.close();
612  // This offer is underfunded
613  env(offer(carol, EUR(1), USD(2)));
614  env.close();
615  env(offer(bob, EUR(2), USD(4), tfPassive));
616  env.close();
617  env.require(offers(bob, 1), offers(carol, 1));
618 
619  std::uint32_t const flags = partialPayment
622 
623  TER const expectedTer =
624  partialPayment ? TER{tesSUCCESS} : TER{tecPATH_PARTIAL};
625 
626  env(pay(alice, bob, USD(5)),
627  path(~USD),
628  sendmax(EUR(10)),
629  txflags(flags),
630  ter(expectedTer));
631  env.close();
632 
633  if (features[fixRmSmallIncreasedQOffers])
634  {
635  if (expectedTer == tesSUCCESS)
636  {
637  env.require(offers(carol, 0));
638  env.require(balance(
639  carol,
640  initialCarolUSD)); // offer is removed but not taken
641  }
642  else
643  {
644  // TODO: Offers are not removed when payments fail
645  // If that is addressed, the test should show that carol's
646  // offer is removed but not taken, as in the other branch of
647  // this if statement
648  }
649  }
650  else
651  {
652  if (partialPayment)
653  {
654  env.require(offers(carol, 0));
655  env.require(
656  balance(carol, USD(0))); // offer is removed and taken
657  }
658  else
659  {
660  // offer is not removed or taken
661  BEAST_EXPECT(isOffer(env, carol, EUR(1), USD(2)));
662  }
663  }
664  }
665  }
666 
667  void
669  {
670  testcase("Enforce No Ripple");
671 
672  using namespace jtx;
673 
674  auto const gw = Account{"gateway"};
675  auto const USD = gw["USD"];
676  auto const BTC = gw["BTC"];
677  auto const EUR = gw["EUR"];
678  Account const alice{"alice"};
679  Account const bob{"bob"};
680  Account const carol{"carol"};
681  Account const dan{"dan"};
682 
683  {
684  // No ripple with an implied account step after an offer
685  Env env{*this, features};
686 
687  auto const gw1 = Account{"gw1"};
688  auto const USD1 = gw1["USD"];
689  auto const gw2 = Account{"gw2"};
690  auto const USD2 = gw2["USD"];
691 
692  env.fund(XRP(10000), alice, noripple(bob), carol, dan, gw1, gw2);
693  env.trust(USD1(1000), alice, carol, dan);
694  env(trust(bob, USD1(1000), tfSetNoRipple));
695  env.trust(USD2(1000), alice, carol, dan);
696  env(trust(bob, USD2(1000), tfSetNoRipple));
697 
698  env(pay(gw1, dan, USD1(50)));
699  env(pay(gw1, bob, USD1(50)));
700  env(pay(gw2, bob, USD2(50)));
701 
702  env(offer(dan, XRP(50), USD1(50)));
703 
704  env(pay(alice, carol, USD2(50)),
705  path(~USD1, bob),
706  sendmax(XRP(50)),
708  ter(tecPATH_DRY));
709  }
710  {
711  // Make sure payment works with default flags
712  Env env{*this, features};
713 
714  auto const gw1 = Account{"gw1"};
715  auto const USD1 = gw1["USD"];
716  auto const gw2 = Account{"gw2"};
717  auto const USD2 = gw2["USD"];
718 
719  env.fund(XRP(10000), alice, bob, carol, dan, gw1, gw2);
720  env.trust(USD1(1000), alice, bob, carol, dan);
721  env.trust(USD2(1000), alice, bob, carol, dan);
722 
723  env(pay(gw1, dan, USD1(50)));
724  env(pay(gw1, bob, USD1(50)));
725  env(pay(gw2, bob, USD2(50)));
726 
727  env(offer(dan, XRP(50), USD1(50)));
728 
729  env(pay(alice, carol, USD2(50)),
730  path(~USD1, bob),
731  sendmax(XRP(50)),
733 
734  env.require(balance(alice, xrpMinusFee(env, 10000 - 50)));
735  env.require(balance(bob, USD1(100)));
736  env.require(balance(bob, USD2(0)));
737  env.require(balance(carol, USD2(50)));
738  }
739  }
740 
741  void
743  {
744  testcase("Insufficient Reserve");
745 
746  // If an account places an offer and its balance
747  // *before* the transaction began isn't high enough
748  // to meet the reserve *after* the transaction runs,
749  // then no offer should go on the books but if the
750  // offer partially or fully crossed the tx succeeds.
751 
752  using namespace jtx;
753 
754  auto const gw = Account{"gateway"};
755  auto const alice = Account{"alice"};
756  auto const bob = Account{"bob"};
757  auto const carol = Account{"carol"};
758  auto const USD = gw["USD"];
759 
760  auto const usdOffer = USD(1000);
761  auto const xrpOffer = XRP(1000);
762 
763  // No crossing:
764  {
765  Env env{*this, features};
766 
767  env.fund(XRP(1000000), gw);
768 
769  auto const f = env.current()->fees().base;
770  auto const r = reserve(env, 0);
771 
772  env.fund(r + f, alice);
773 
774  env(trust(alice, usdOffer), ter(tesSUCCESS));
775  env(pay(gw, alice, usdOffer), ter(tesSUCCESS));
776  env(offer(alice, xrpOffer, usdOffer), ter(tecINSUF_RESERVE_OFFER));
777 
778  env.require(balance(alice, r - f), owners(alice, 1));
779  }
780 
781  // Partial cross:
782  {
783  Env env{*this, features};
784 
785  env.fund(XRP(1000000), gw);
786 
787  auto const f = env.current()->fees().base;
788  auto const r = reserve(env, 0);
789 
790  auto const usdOffer2 = USD(500);
791  auto const xrpOffer2 = XRP(500);
792 
793  env.fund(r + f + xrpOffer, bob);
794  env(offer(bob, usdOffer2, xrpOffer2), ter(tesSUCCESS));
795  env.fund(r + f, alice);
796  env(trust(alice, usdOffer), ter(tesSUCCESS));
797  env(pay(gw, alice, usdOffer), ter(tesSUCCESS));
798  env(offer(alice, xrpOffer, usdOffer), ter(tesSUCCESS));
799 
800  env.require(
801  balance(alice, r - f + xrpOffer2),
802  balance(alice, usdOffer2),
803  owners(alice, 1),
804  balance(bob, r + xrpOffer2),
805  balance(bob, usdOffer2),
806  owners(bob, 1));
807  }
808 
809  // Account has enough reserve as is, but not enough
810  // if an offer were added. Attempt to sell IOUs to
811  // buy XRP. If it fully crosses, we succeed.
812  {
813  Env env{*this, features};
814 
815  env.fund(XRP(1000000), gw);
816 
817  auto const f = env.current()->fees().base;
818  auto const r = reserve(env, 0);
819 
820  auto const usdOffer2 = USD(500);
821  auto const xrpOffer2 = XRP(500);
822 
823  env.fund(r + f + xrpOffer, bob, carol);
824  env(offer(bob, usdOffer2, xrpOffer2), ter(tesSUCCESS));
825  env(offer(carol, usdOffer, xrpOffer), ter(tesSUCCESS));
826 
827  env.fund(r + f, alice);
828  env(trust(alice, usdOffer), ter(tesSUCCESS));
829  env(pay(gw, alice, usdOffer), ter(tesSUCCESS));
830  env(offer(alice, xrpOffer, usdOffer), ter(tesSUCCESS));
831 
832  env.require(
833  balance(alice, r - f + xrpOffer),
834  balance(alice, USD(0)),
835  owners(alice, 1),
836  balance(bob, r + xrpOffer2),
837  balance(bob, usdOffer2),
838  owners(bob, 1),
839  balance(carol, r + xrpOffer2),
840  balance(carol, usdOffer2),
841  owners(carol, 2));
842  }
843  }
844 
845  // Helper function that returns the Offers on an account.
848  {
850  forEachItem(
851  *env.current(),
852  account,
853  [&result](std::shared_ptr<SLE const> const& sle) {
854  if (sle->getType() == ltOFFER)
855  result.push_back(sle);
856  });
857  return result;
858  }
859 
860  void
862  {
863  testcase("Fill Modes");
864 
865  using namespace jtx;
866 
867  auto const startBalance = XRP(1000000);
868  auto const gw = Account{"gateway"};
869  auto const alice = Account{"alice"};
870  auto const bob = Account{"bob"};
871  auto const USD = gw["USD"];
872 
873  // Fill or Kill - unless we fully cross, just charge a fee and don't
874  // place the offer on the books. But also clean up expired offers
875  // that are discovered along the way.
876  //
877  // fix1578 changes the return code. Verify expected behavior
878  // without and with fix1578.
879  for (auto const& tweakedFeatures :
880  {features - fix1578, features | fix1578})
881  {
882  Env env{*this, tweakedFeatures};
883 
884  auto const f = env.current()->fees().base;
885 
886  env.fund(startBalance, gw, alice, bob);
887 
888  // bob creates an offer that expires before the next ledger close.
889  env(offer(bob, USD(500), XRP(500)),
891  ter(tesSUCCESS));
892 
893  // The offer expires (it's not removed yet).
894  env.close();
895  env.require(owners(bob, 1), offers(bob, 1));
896 
897  // bob creates the offer that will be crossed.
898  env(offer(bob, USD(500), XRP(500)), ter(tesSUCCESS));
899  env.close();
900  env.require(owners(bob, 2), offers(bob, 2));
901 
902  env(trust(alice, USD(1000)), ter(tesSUCCESS));
903  env(pay(gw, alice, USD(1000)), ter(tesSUCCESS));
904 
905  // Order that can't be filled but will remove bob's expired offer:
906  {
907  TER const killedCode{
908  tweakedFeatures[fix1578] ? TER{tecKILLED}
909  : TER{tesSUCCESS}};
910  env(offer(alice, XRP(1000), USD(1000)),
912  ter(killedCode));
913  }
914  env.require(
915  balance(alice, startBalance - (f * 2)),
916  balance(alice, USD(1000)),
917  owners(alice, 1),
918  offers(alice, 0),
919  balance(bob, startBalance - (f * 2)),
920  balance(bob, USD(none)),
921  owners(bob, 1),
922  offers(bob, 1));
923 
924  // Order that can be filled
925  env(offer(alice, XRP(500), USD(500)),
927  ter(tesSUCCESS));
928 
929  env.require(
930  balance(alice, startBalance - (f * 3) + XRP(500)),
931  balance(alice, USD(500)),
932  owners(alice, 1),
933  offers(alice, 0),
934  balance(bob, startBalance - (f * 2) - XRP(500)),
935  balance(bob, USD(500)),
936  owners(bob, 1),
937  offers(bob, 0));
938  }
939 
940  // Immediate or Cancel - cross as much as possible
941  // and add nothing on the books:
942  {
943  Env env{*this, features};
944 
945  auto const f = env.current()->fees().base;
946 
947  env.fund(startBalance, gw, alice, bob);
948 
949  env(trust(alice, USD(1000)), ter(tesSUCCESS));
950  env(pay(gw, alice, USD(1000)), ter(tesSUCCESS));
951 
952  // No cross:
953  {
954  TER const expectedCode = features[featureImmediateOfferKilled]
955  ? static_cast<TER>(tecKILLED)
956  : static_cast<TER>(tesSUCCESS);
957  env(offer(alice, XRP(1000), USD(1000)),
959  ter(expectedCode));
960  }
961 
962  env.require(
963  balance(alice, startBalance - f - f),
964  balance(alice, USD(1000)),
965  owners(alice, 1),
966  offers(alice, 0));
967 
968  // Partially cross:
969  env(offer(bob, USD(50), XRP(50)), ter(tesSUCCESS));
970  env(offer(alice, XRP(1000), USD(1000)),
972  ter(tesSUCCESS));
973 
974  env.require(
975  balance(alice, startBalance - f - f - f + XRP(50)),
976  balance(alice, USD(950)),
977  owners(alice, 1),
978  offers(alice, 0),
979  balance(bob, startBalance - f - XRP(50)),
980  balance(bob, USD(50)),
981  owners(bob, 1),
982  offers(bob, 0));
983 
984  // Fully cross:
985  env(offer(bob, USD(50), XRP(50)), ter(tesSUCCESS));
986  env(offer(alice, XRP(50), USD(50)),
988  ter(tesSUCCESS));
989 
990  env.require(
991  balance(alice, startBalance - f - f - f - f + XRP(100)),
992  balance(alice, USD(900)),
993  owners(alice, 1),
994  offers(alice, 0),
995  balance(bob, startBalance - f - f - XRP(100)),
996  balance(bob, USD(100)),
997  owners(bob, 1),
998  offers(bob, 0));
999  }
1000 
1001  // tfPassive -- place the offer without crossing it.
1002  {
1003  Env env(*this, features);
1004 
1005  env.fund(startBalance, gw, alice, bob);
1006  env.close();
1007 
1008  env(trust(bob, USD(1000)));
1009  env.close();
1010 
1011  env(pay(gw, bob, USD(1000)));
1012  env.close();
1013 
1014  env(offer(alice, USD(1000), XRP(2000)));
1015  env.close();
1016 
1017  auto const aliceOffers = offersOnAccount(env, alice);
1018  BEAST_EXPECT(aliceOffers.size() == 1);
1019  for (auto offerPtr : aliceOffers)
1020  {
1021  auto const& offer = *offerPtr;
1022  BEAST_EXPECT(offer[sfTakerGets] == XRP(2000));
1023  BEAST_EXPECT(offer[sfTakerPays] == USD(1000));
1024  }
1025 
1026  // bob creates a passive offer that could cross alice's.
1027  // bob's offer should stay in the ledger.
1028  env(offer(bob, XRP(2000), USD(1000), tfPassive));
1029  env.close();
1030  env.require(offers(alice, 1));
1031 
1032  auto const bobOffers = offersOnAccount(env, bob);
1033  BEAST_EXPECT(bobOffers.size() == 1);
1034  for (auto offerPtr : bobOffers)
1035  {
1036  auto const& offer = *offerPtr;
1037  BEAST_EXPECT(offer[sfTakerGets] == USD(1000));
1038  BEAST_EXPECT(offer[sfTakerPays] == XRP(2000));
1039  }
1040 
1041  // It should be possible for gw to cross both of those offers.
1042  env(offer(gw, XRP(2000), USD(1000)));
1043  env.close();
1044  env.require(offers(alice, 0));
1045  env.require(offers(gw, 0));
1046  env.require(offers(bob, 1));
1047 
1048  env(offer(gw, USD(1000), XRP(2000)));
1049  env.close();
1050  env.require(offers(bob, 0));
1051  env.require(offers(gw, 0));
1052  }
1053 
1054  // tfPassive -- cross only offers of better quality.
1055  {
1056  Env env(*this, features);
1057 
1058  env.fund(startBalance, gw, "alice", "bob");
1059  env.close();
1060 
1061  env(trust("bob", USD(1000)));
1062  env.close();
1063 
1064  env(pay(gw, "bob", USD(1000)));
1065  env(offer("alice", USD(500), XRP(1001)));
1066  env.close();
1067 
1068  env(offer("alice", USD(500), XRP(1000)));
1069  env.close();
1070 
1071  auto const aliceOffers = offersOnAccount(env, "alice");
1072  BEAST_EXPECT(aliceOffers.size() == 2);
1073 
1074  // bob creates a passive offer. That offer should cross one
1075  // of alice's (the one with better quality) and leave alice's
1076  // other offer untouched.
1077  env(offer("bob", XRP(2000), USD(1000), tfPassive));
1078  env.close();
1079  env.require(offers("alice", 1));
1080 
1081  auto const bobOffers = offersOnAccount(env, "bob");
1082  BEAST_EXPECT(bobOffers.size() == 1);
1083  for (auto offerPtr : bobOffers)
1084  {
1085  auto const& offer = *offerPtr;
1086  BEAST_EXPECT(offer[sfTakerGets] == USD(499.5));
1087  BEAST_EXPECT(offer[sfTakerPays] == XRP(999));
1088  }
1089  }
1090  }
1091 
1092  void
1094  {
1095  testcase("Malformed Detection");
1096 
1097  using namespace jtx;
1098 
1099  auto const startBalance = XRP(1000000);
1100  auto const gw = Account{"gateway"};
1101  auto const alice = Account{"alice"};
1102  auto const USD = gw["USD"];
1103 
1104  Env env{*this, features};
1105 
1106  env.fund(startBalance, gw, alice);
1107 
1108  // Order that has invalid flags
1109  env(offer(alice, USD(1000), XRP(1000)),
1111  ter(temINVALID_FLAG));
1112  env.require(
1113  balance(alice, startBalance), owners(alice, 0), offers(alice, 0));
1114 
1115  // Order with incompatible flags
1116  env(offer(alice, USD(1000), XRP(1000)),
1118  ter(temINVALID_FLAG));
1119  env.require(
1120  balance(alice, startBalance), owners(alice, 0), offers(alice, 0));
1121 
1122  // Sell and buy the same asset
1123  {
1124  // Alice tries an XRP to XRP order:
1125  env(offer(alice, XRP(1000), XRP(1000)), ter(temBAD_OFFER));
1126  env.require(owners(alice, 0), offers(alice, 0));
1127 
1128  // Alice tries an IOU to IOU order:
1129  env(trust(alice, USD(1000)), ter(tesSUCCESS));
1130  env(pay(gw, alice, USD(1000)), ter(tesSUCCESS));
1131  env(offer(alice, USD(1000), USD(1000)), ter(temREDUNDANT));
1132  env.require(owners(alice, 1), offers(alice, 0));
1133  }
1134 
1135  // Offers with negative amounts
1136  {
1137  env(offer(alice, -USD(1000), XRP(1000)), ter(temBAD_OFFER));
1138  env.require(owners(alice, 1), offers(alice, 0));
1139 
1140  env(offer(alice, USD(1000), -XRP(1000)), ter(temBAD_OFFER));
1141  env.require(owners(alice, 1), offers(alice, 0));
1142  }
1143 
1144  // Offer with a bad expiration
1145  {
1146  env(offer(alice, USD(1000), XRP(1000)),
1149  env.require(owners(alice, 1), offers(alice, 0));
1150  }
1151 
1152  // Offer with a bad offer sequence
1153  {
1154  env(offer(alice, USD(1000), XRP(1000)),
1155  json(jss::OfferSequence, std::uint32_t(0)),
1156  ter(temBAD_SEQUENCE));
1157  env.require(owners(alice, 1), offers(alice, 0));
1158  }
1159 
1160  // Use XRP as a currency code
1161  {
1162  auto const BAD = IOU(gw, badCurrency());
1163 
1164  env(offer(alice, XRP(1000), BAD(1000)), ter(temBAD_CURRENCY));
1165  env.require(owners(alice, 1), offers(alice, 0));
1166  }
1167  }
1168 
1169  void
1171  {
1172  testcase("Offer Expiration");
1173 
1174  using namespace jtx;
1175 
1176  auto const gw = Account{"gateway"};
1177  auto const alice = Account{"alice"};
1178  auto const bob = Account{"bob"};
1179  auto const USD = gw["USD"];
1180 
1181  auto const startBalance = XRP(1000000);
1182  auto const usdOffer = USD(1000);
1183  auto const xrpOffer = XRP(1000);
1184 
1185  Env env{*this, features};
1186 
1187  env.fund(startBalance, gw, alice, bob);
1188  env.close();
1189 
1190  auto const f = env.current()->fees().base;
1191 
1192  env(trust(alice, usdOffer), ter(tesSUCCESS));
1193  env(pay(gw, alice, usdOffer), ter(tesSUCCESS));
1194  env.close();
1195  env.require(
1196  balance(alice, startBalance - f),
1197  balance(alice, usdOffer),
1198  offers(alice, 0),
1199  owners(alice, 1));
1200 
1201  // Place an offer that should have already expired.
1202  // The DepositPreauth amendment changes the return code; adapt to that.
1203  bool const featPreauth{features[featureDepositPreauth]};
1204 
1205  env(offer(alice, xrpOffer, usdOffer),
1207  ter(featPreauth ? TER{tecEXPIRED} : TER{tesSUCCESS}));
1208 
1209  env.require(
1210  balance(alice, startBalance - f - f),
1211  balance(alice, usdOffer),
1212  offers(alice, 0),
1213  owners(alice, 1));
1214  env.close();
1215 
1216  // Add an offer that expires before the next ledger close
1217  env(offer(alice, xrpOffer, usdOffer),
1218  json(sfExpiration.fieldName, lastClose(env) + 1),
1219  ter(tesSUCCESS));
1220  env.require(
1221  balance(alice, startBalance - f - f - f),
1222  balance(alice, usdOffer),
1223  offers(alice, 1),
1224  owners(alice, 2));
1225 
1226  // The offer expires (it's not removed yet)
1227  env.close();
1228  env.require(
1229  balance(alice, startBalance - f - f - f),
1230  balance(alice, usdOffer),
1231  offers(alice, 1),
1232  owners(alice, 2));
1233 
1234  // Add offer - the expired offer is removed
1235  env(offer(bob, usdOffer, xrpOffer), ter(tesSUCCESS));
1236  env.require(
1237  balance(alice, startBalance - f - f - f),
1238  balance(alice, usdOffer),
1239  offers(alice, 0),
1240  owners(alice, 1),
1241  balance(bob, startBalance - f),
1242  balance(bob, USD(none)),
1243  offers(bob, 1),
1244  owners(bob, 1));
1245  }
1246 
1247  void
1249  {
1250  testcase("Unfunded Crossing");
1251 
1252  using namespace jtx;
1253 
1254  auto const gw = Account{"gateway"};
1255  auto const USD = gw["USD"];
1256 
1257  auto const usdOffer = USD(1000);
1258  auto const xrpOffer = XRP(1000);
1259 
1260  Env env{*this, features};
1261 
1262  env.fund(XRP(1000000), gw);
1263 
1264  // The fee that's charged for transactions
1265  auto const f = env.current()->fees().base;
1266 
1267  // Account is at the reserve, and will dip below once
1268  // fees are subtracted.
1269  env.fund(reserve(env, 0), "alice");
1270  env(offer("alice", usdOffer, xrpOffer), ter(tecUNFUNDED_OFFER));
1271  env.require(balance("alice", reserve(env, 0) - f), owners("alice", 0));
1272 
1273  // Account has just enough for the reserve and the
1274  // fee.
1275  env.fund(reserve(env, 0) + f, "bob");
1276  env(offer("bob", usdOffer, xrpOffer), ter(tecUNFUNDED_OFFER));
1277  env.require(balance("bob", reserve(env, 0)), owners("bob", 0));
1278 
1279  // Account has enough for the reserve, the fee and
1280  // the offer, and a bit more, but not enough for the
1281  // reserve after the offer is placed.
1282  env.fund(reserve(env, 0) + f + XRP(1), "carol");
1283  env(offer("carol", usdOffer, xrpOffer), ter(tecINSUF_RESERVE_OFFER));
1284  env.require(
1285  balance("carol", reserve(env, 0) + XRP(1)), owners("carol", 0));
1286 
1287  // Account has enough for the reserve plus one
1288  // offer, and the fee.
1289  env.fund(reserve(env, 1) + f, "dan");
1290  env(offer("dan", usdOffer, xrpOffer), ter(tesSUCCESS));
1291  env.require(balance("dan", reserve(env, 1)), owners("dan", 1));
1292 
1293  // Account has enough for the reserve plus one
1294  // offer, the fee and the entire offer amount.
1295  env.fund(reserve(env, 1) + f + xrpOffer, "eve");
1296  env(offer("eve", usdOffer, xrpOffer), ter(tesSUCCESS));
1297  env.require(
1298  balance("eve", reserve(env, 1) + xrpOffer), owners("eve", 1));
1299  }
1300 
1301  void
1302  testSelfCross(bool use_partner, FeatureBitset features)
1303  {
1304  testcase(
1305  std::string("Self-crossing") +
1306  (use_partner ? ", with partner account" : ""));
1307 
1308  using namespace jtx;
1309 
1310  auto const gw = Account{"gateway"};
1311  auto const partner = Account{"partner"};
1312  auto const USD = gw["USD"];
1313  auto const BTC = gw["BTC"];
1314 
1315  Env env{*this, features};
1316  env.close();
1317 
1318  env.fund(XRP(10000), gw);
1319  if (use_partner)
1320  {
1321  env.fund(XRP(10000), partner);
1322  env(trust(partner, USD(100)));
1323  env(trust(partner, BTC(500)));
1324  env(pay(gw, partner, USD(100)));
1325  env(pay(gw, partner, BTC(500)));
1326  }
1327  auto const& account_to_test = use_partner ? partner : gw;
1328 
1329  env.close();
1330  env.require(offers(account_to_test, 0));
1331 
1332  // PART 1:
1333  // we will make two offers that can be used to bridge BTC to USD
1334  // through XRP
1335  env(offer(account_to_test, BTC(250), XRP(1000)));
1336  env.require(offers(account_to_test, 1));
1337 
1338  // validate that the book now shows a BTC for XRP offer
1339  BEAST_EXPECT(isOffer(env, account_to_test, BTC(250), XRP(1000)));
1340 
1341  auto const secondLegSeq = env.seq(account_to_test);
1342  env(offer(account_to_test, XRP(1000), USD(50)));
1343  env.require(offers(account_to_test, 2));
1344 
1345  // validate that the book also shows a XRP for USD offer
1346  BEAST_EXPECT(isOffer(env, account_to_test, XRP(1000), USD(50)));
1347 
1348  // now make an offer that will cross and auto-bridge, meaning
1349  // the outstanding offers will be taken leaving us with none
1350  env(offer(account_to_test, USD(50), BTC(250)));
1351 
1352  auto jrr = getBookOffers(env, USD, BTC);
1353  BEAST_EXPECT(jrr[jss::offers].isArray());
1354  BEAST_EXPECT(jrr[jss::offers].size() == 0);
1355 
1356  jrr = getBookOffers(env, BTC, XRP);
1357  BEAST_EXPECT(jrr[jss::offers].isArray());
1358  BEAST_EXPECT(jrr[jss::offers].size() == 0);
1359 
1360  // NOTE :
1361  // At this point, all offers are expected to be consumed.
1362  // Alas, they are not - because of a bug in the Taker auto-bridging
1363  // implementation which is addressed by fixTakerDryOfferRemoval.
1364  // The pre-fixTakerDryOfferRemoval implementation (incorrect) leaves
1365  // an empty offer in the second leg of the bridge. Validate both the
1366  // old and the new behavior.
1367  {
1368  auto acctOffers = offersOnAccount(env, account_to_test);
1369  bool const noStaleOffers{
1370  features[featureFlowCross] ||
1371  features[fixTakerDryOfferRemoval]};
1372 
1373  BEAST_EXPECT(acctOffers.size() == (noStaleOffers ? 0 : 1));
1374  for (auto const& offerPtr : acctOffers)
1375  {
1376  auto const& offer = *offerPtr;
1377  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
1378  BEAST_EXPECT(offer[sfTakerGets] == USD(0));
1379  BEAST_EXPECT(offer[sfTakerPays] == XRP(0));
1380  }
1381  }
1382 
1383  // cancel that lingering second offer so that it doesn't interfere
1384  // with the next set of offers we test. This will not be needed once
1385  // the bridging bug is fixed
1386  env(offer_cancel(account_to_test, secondLegSeq));
1387  env.require(offers(account_to_test, 0));
1388 
1389  // PART 2:
1390  // simple direct crossing BTC to USD and then USD to BTC which causes
1391  // the first offer to be replaced
1392  env(offer(account_to_test, BTC(250), USD(50)));
1393  env.require(offers(account_to_test, 1));
1394 
1395  // validate that the book shows one BTC for USD offer and no USD for
1396  // BTC offers
1397  BEAST_EXPECT(isOffer(env, account_to_test, BTC(250), USD(50)));
1398 
1399  jrr = getBookOffers(env, USD, BTC);
1400  BEAST_EXPECT(jrr[jss::offers].isArray());
1401  BEAST_EXPECT(jrr[jss::offers].size() == 0);
1402 
1403  // this second offer would self-cross directly, so it causes the first
1404  // offer by the same owner/taker to be removed
1405  env(offer(account_to_test, USD(50), BTC(250)));
1406  env.require(offers(account_to_test, 1));
1407 
1408  // validate that we now have just the second offer...the first
1409  // was removed
1410  jrr = getBookOffers(env, BTC, USD);
1411  BEAST_EXPECT(jrr[jss::offers].isArray());
1412  BEAST_EXPECT(jrr[jss::offers].size() == 0);
1413 
1414  BEAST_EXPECT(isOffer(env, account_to_test, USD(50), BTC(250)));
1415  }
1416 
1417  void
1419  {
1420  // This test creates an offer test for negative balance
1421  // with transfer fees and miniscule funds.
1422  testcase("Negative Balance");
1423 
1424  using namespace jtx;
1425 
1426  Env env{*this, features};
1427 
1428  auto const gw = Account{"gateway"};
1429  auto const alice = Account{"alice"};
1430  auto const bob = Account{"bob"};
1431  auto const USD = gw["USD"];
1432  auto const BTC = gw["BTC"];
1433 
1434  // these *interesting* amounts were taken
1435  // from the original JS test that was ported here
1436  auto const gw_initial_balance = drops(1149999730);
1437  auto const alice_initial_balance = drops(499946999680);
1438  auto const bob_initial_balance = drops(10199999920);
1439  auto const small_amount =
1440  STAmount{bob["USD"].issue(), UINT64_C(2710505431213761), -33};
1441 
1442  env.fund(gw_initial_balance, gw);
1443  env.fund(alice_initial_balance, alice);
1444  env.fund(bob_initial_balance, bob);
1445 
1446  env(rate(gw, 1.005));
1447 
1448  env(trust(alice, USD(500)));
1449  env(trust(bob, USD(50)));
1450  env(trust(gw, alice["USD"](100)));
1451 
1452  env(pay(gw, alice, alice["USD"](50)));
1453  env(pay(gw, bob, small_amount));
1454 
1455  env(offer(alice, USD(50), XRP(150000)));
1456 
1457  // unfund the offer
1458  env(pay(alice, gw, USD(100)));
1459 
1460  // drop the trust line (set to 0)
1461  env(trust(gw, alice["USD"](0)));
1462 
1463  // verify balances
1464  auto jrr = ledgerEntryState(env, alice, gw, "USD");
1465  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "50");
1466 
1467  jrr = ledgerEntryState(env, bob, gw, "USD");
1468  BEAST_EXPECT(
1469  jrr[jss::node][sfBalance.fieldName][jss::value] ==
1470  "-2710505431213761e-33");
1471 
1472  // create crossing offer
1473  env(offer(bob, XRP(2000), USD(1)));
1474 
1475  // verify balances again.
1476  //
1477  // NOTE :
1478  // Here a difference in the rounding modes of our two offer crossing
1479  // algorithms becomes apparent. The old offer crossing would consume
1480  // small_amount and transfer no XRP. The new offer crossing transfers
1481  // a single drop, rather than no drops.
1482  auto const crossingDelta =
1483  features[featureFlowCross] ? drops(1) : drops(0);
1484 
1485  jrr = ledgerEntryState(env, alice, gw, "USD");
1486  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "50");
1487  BEAST_EXPECT(
1488  env.balance(alice, xrpIssue()) ==
1489  alice_initial_balance - env.current()->fees().base * 3 -
1490  crossingDelta);
1491 
1492  jrr = ledgerEntryState(env, bob, gw, "USD");
1493  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "0");
1494  BEAST_EXPECT(
1495  env.balance(bob, xrpIssue()) ==
1496  bob_initial_balance - env.current()->fees().base * 2 +
1497  crossingDelta);
1498  }
1499 
1500  void
1501  testOfferCrossWithXRP(bool reverse_order, FeatureBitset features)
1502  {
1503  testcase(
1504  std::string("Offer Crossing with XRP, ") +
1505  (reverse_order ? "Reverse" : "Normal") + " order");
1506 
1507  using namespace jtx;
1508 
1509  Env env{*this, features};
1510 
1511  auto const gw = Account{"gateway"};
1512  auto const alice = Account{"alice"};
1513  auto const bob = Account{"bob"};
1514  auto const USD = gw["USD"];
1515 
1516  env.fund(XRP(10000), gw, alice, bob);
1517 
1518  env(trust(alice, USD(1000)));
1519  env(trust(bob, USD(1000)));
1520 
1521  env(pay(gw, alice, alice["USD"](500)));
1522 
1523  if (reverse_order)
1524  env(offer(bob, USD(1), XRP(4000)));
1525 
1526  env(offer(alice, XRP(150000), USD(50)));
1527 
1528  if (!reverse_order)
1529  env(offer(bob, USD(1), XRP(4000)));
1530 
1531  // Existing offer pays better than this wants.
1532  // Fully consume existing offer.
1533  // Pay 1 USD, get 4000 XRP.
1534 
1535  auto jrr = ledgerEntryState(env, bob, gw, "USD");
1536  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-1");
1537  jrr = ledgerEntryRoot(env, bob);
1538  BEAST_EXPECT(
1539  jrr[jss::node][sfBalance.fieldName] ==
1540  to_string((XRP(10000) - XRP(reverse_order ? 4000 : 3000) -
1541  env.current()->fees().base * 2)
1542  .xrp()));
1543 
1544  jrr = ledgerEntryState(env, alice, gw, "USD");
1545  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-499");
1546  jrr = ledgerEntryRoot(env, alice);
1547  BEAST_EXPECT(
1548  jrr[jss::node][sfBalance.fieldName] ==
1549  to_string((XRP(10000) + XRP(reverse_order ? 4000 : 3000) -
1550  env.current()->fees().base * 2)
1551  .xrp()));
1552  }
1553 
1554  void
1556  {
1557  testcase("Offer Crossing with Limit Override");
1558 
1559  using namespace jtx;
1560 
1561  Env env{*this, features};
1562 
1563  auto const gw = Account{"gateway"};
1564  auto const alice = Account{"alice"};
1565  auto const bob = Account{"bob"};
1566  auto const USD = gw["USD"];
1567 
1568  env.fund(XRP(100000), gw, alice, bob);
1569 
1570  env(trust(alice, USD(1000)));
1571 
1572  env(pay(gw, alice, alice["USD"](500)));
1573 
1574  env(offer(alice, XRP(150000), USD(50)));
1575  env(offer(bob, USD(1), XRP(3000)));
1576 
1577  auto jrr = ledgerEntryState(env, bob, gw, "USD");
1578  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-1");
1579  jrr = ledgerEntryRoot(env, bob);
1580  BEAST_EXPECT(
1581  jrr[jss::node][sfBalance.fieldName] ==
1582  to_string((XRP(100000) - XRP(3000) - env.current()->fees().base * 1)
1583  .xrp()));
1584 
1585  jrr = ledgerEntryState(env, alice, gw, "USD");
1586  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-499");
1587  jrr = ledgerEntryRoot(env, alice);
1588  BEAST_EXPECT(
1589  jrr[jss::node][sfBalance.fieldName] ==
1590  to_string((XRP(100000) + XRP(3000) - env.current()->fees().base * 2)
1591  .xrp()));
1592  }
1593 
1594  void
1596  {
1597  testcase("Offer Accept then Cancel.");
1598 
1599  using namespace jtx;
1600 
1601  Env env{*this, features};
1602 
1603  auto const USD = env.master["USD"];
1604 
1605  auto const nextOfferSeq = env.seq(env.master);
1606  env(offer(env.master, XRP(500), USD(100)));
1607  env.close();
1608 
1609  env(offer_cancel(env.master, nextOfferSeq));
1610  BEAST_EXPECT(env.seq(env.master) == nextOfferSeq + 2);
1611 
1612  // ledger_accept, call twice and verify no odd behavior
1613  env.close();
1614  env.close();
1615  BEAST_EXPECT(env.seq(env.master) == nextOfferSeq + 2);
1616  }
1617 
1618  void
1620  {
1621  testcase("Offer Cancel Past and Future Sequence.");
1622 
1623  using namespace jtx;
1624 
1625  Env env{*this, features};
1626 
1627  auto const alice = Account{"alice"};
1628 
1629  auto const nextOfferSeq = env.seq(env.master);
1630  env.fund(XRP(10000), alice);
1631 
1632  env(offer_cancel(env.master, nextOfferSeq));
1633 
1634  env(offer_cancel(env.master, env.seq(env.master)),
1635  ter(temBAD_SEQUENCE));
1636 
1637  env(offer_cancel(env.master, env.seq(env.master) + 1),
1638  ter(temBAD_SEQUENCE));
1639 
1640  env.close();
1641  }
1642 
1643  void
1645  {
1646  testcase("Currency Conversion: Entire Offer");
1647 
1648  using namespace jtx;
1649 
1650  Env env{*this, features};
1651 
1652  auto const gw = Account{"gateway"};
1653  auto const alice = Account{"alice"};
1654  auto const bob = Account{"bob"};
1655  auto const USD = gw["USD"];
1656 
1657  env.fund(XRP(10000), gw, alice, bob);
1658  env.require(owners(bob, 0));
1659 
1660  env(trust(alice, USD(100)));
1661  env(trust(bob, USD(1000)));
1662 
1663  env.require(owners(alice, 1), owners(bob, 1));
1664 
1665  env(pay(gw, alice, alice["USD"](100)));
1666  auto const bobOfferSeq = env.seq(bob);
1667  env(offer(bob, USD(100), XRP(500)));
1668 
1669  env.require(owners(alice, 1), owners(bob, 2));
1670  auto jro = ledgerEntryOffer(env, bob, bobOfferSeq);
1671  BEAST_EXPECT(
1672  jro[jss::node][jss::TakerGets] == XRP(500).value().getText());
1673  BEAST_EXPECT(
1674  jro[jss::node][jss::TakerPays] ==
1675  USD(100).value().getJson(JsonOptions::none));
1676 
1677  env(pay(alice, alice, XRP(500)), sendmax(USD(100)));
1678 
1679  auto jrr = ledgerEntryState(env, alice, gw, "USD");
1680  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "0");
1681  jrr = ledgerEntryRoot(env, alice);
1682  BEAST_EXPECT(
1683  jrr[jss::node][sfBalance.fieldName] ==
1684  to_string((XRP(10000) + XRP(500) - env.current()->fees().base * 2)
1685  .xrp()));
1686 
1687  jrr = ledgerEntryState(env, bob, gw, "USD");
1688  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
1689 
1690  jro = ledgerEntryOffer(env, bob, bobOfferSeq);
1691  BEAST_EXPECT(jro[jss::error] == "entryNotFound");
1692 
1693  env.require(owners(alice, 1), owners(bob, 1));
1694  }
1695 
1696  void
1698  {
1699  testcase("Currency Conversion: Offerer Into Debt");
1700 
1701  using namespace jtx;
1702 
1703  Env env{*this, features};
1704 
1705  auto const alice = Account{"alice"};
1706  auto const bob = Account{"bob"};
1707  auto const carol = Account{"carol"};
1708 
1709  env.fund(XRP(10000), alice, bob, carol);
1710 
1711  env(trust(alice, carol["EUR"](2000)));
1712  env(trust(bob, alice["USD"](100)));
1713  env(trust(carol, bob["EUR"](1000)));
1714 
1715  auto const bobOfferSeq = env.seq(bob);
1716  env(offer(bob, alice["USD"](50), carol["EUR"](200)),
1718 
1719  env(offer(alice, carol["EUR"](200), alice["USD"](50)));
1720 
1721  auto jro = ledgerEntryOffer(env, bob, bobOfferSeq);
1722  BEAST_EXPECT(jro[jss::error] == "entryNotFound");
1723  }
1724 
1725  void
1727  {
1728  testcase("Currency Conversion: In Parts");
1729 
1730  using namespace jtx;
1731 
1732  Env env{*this, features};
1733 
1734  auto const gw = Account{"gateway"};
1735  auto const alice = Account{"alice"};
1736  auto const bob = Account{"bob"};
1737  auto const USD = gw["USD"];
1738 
1739  env.fund(XRP(10000), gw, alice, bob);
1740 
1741  env(trust(alice, USD(200)));
1742  env(trust(bob, USD(1000)));
1743 
1744  env(pay(gw, alice, alice["USD"](200)));
1745 
1746  auto const bobOfferSeq = env.seq(bob);
1747  env(offer(bob, USD(100), XRP(500)));
1748 
1749  env(pay(alice, alice, XRP(200)), sendmax(USD(100)));
1750 
1751  // The previous payment reduced the remaining offer amount by 200 XRP
1752  auto jro = ledgerEntryOffer(env, bob, bobOfferSeq);
1753  BEAST_EXPECT(
1754  jro[jss::node][jss::TakerGets] == XRP(300).value().getText());
1755  BEAST_EXPECT(
1756  jro[jss::node][jss::TakerPays] ==
1757  USD(60).value().getJson(JsonOptions::none));
1758 
1759  // the balance between alice and gw is 160 USD..200 less the 40 taken
1760  // by the offer
1761  auto jrr = ledgerEntryState(env, alice, gw, "USD");
1762  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-160");
1763  // alice now has 200 more XRP from the payment
1764  jrr = ledgerEntryRoot(env, alice);
1765  BEAST_EXPECT(
1766  jrr[jss::node][sfBalance.fieldName] ==
1767  to_string((XRP(10000) + XRP(200) - env.current()->fees().base * 2)
1768  .xrp()));
1769 
1770  // bob got 40 USD from partial consumption of the offer
1771  jrr = ledgerEntryState(env, bob, gw, "USD");
1772  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-40");
1773 
1774  // Alice converts USD to XRP which should fail
1775  // due to PartialPayment.
1776  env(pay(alice, alice, XRP(600)),
1777  sendmax(USD(100)),
1778  ter(tecPATH_PARTIAL));
1779 
1780  // Alice converts USD to XRP, should succeed because
1781  // we permit partial payment
1782  env(pay(alice, alice, XRP(600)),
1783  sendmax(USD(100)),
1785 
1786  // Verify the offer was consumed
1787  jro = ledgerEntryOffer(env, bob, bobOfferSeq);
1788  BEAST_EXPECT(jro[jss::error] == "entryNotFound");
1789 
1790  // verify balances look right after the partial payment
1791  // only 300 XRP should be have been payed since that's all
1792  // that remained in the offer from bob. The alice balance is now
1793  // 100 USD because another 60 USD were transferred to bob in the second
1794  // payment
1795  jrr = ledgerEntryState(env, alice, gw, "USD");
1796  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
1797  jrr = ledgerEntryRoot(env, alice);
1798  BEAST_EXPECT(
1799  jrr[jss::node][sfBalance.fieldName] ==
1800  to_string((XRP(10000) + XRP(200) + XRP(300) -
1801  env.current()->fees().base * 4)
1802  .xrp()));
1803 
1804  // bob now has 100 USD - 40 from the first payment and 60 from the
1805  // second (partial) payment
1806  jrr = ledgerEntryState(env, bob, gw, "USD");
1807  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
1808  }
1809 
1810  void
1812  {
1813  testcase("Cross Currency Payment: Start with XRP");
1814 
1815  using namespace jtx;
1816 
1817  Env env{*this, features};
1818 
1819  auto const gw = Account{"gateway"};
1820  auto const alice = Account{"alice"};
1821  auto const bob = Account{"bob"};
1822  auto const carol = Account{"carol"};
1823  auto const USD = gw["USD"];
1824 
1825  env.fund(XRP(10000), gw, alice, bob, carol);
1826 
1827  env(trust(carol, USD(1000)));
1828  env(trust(bob, USD(2000)));
1829 
1830  env(pay(gw, carol, carol["USD"](500)));
1831 
1832  auto const carolOfferSeq = env.seq(carol);
1833  env(offer(carol, XRP(500), USD(50)));
1834 
1835  env(pay(alice, bob, USD(25)), sendmax(XRP(333)));
1836 
1837  auto jrr = ledgerEntryState(env, bob, gw, "USD");
1838  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-25");
1839 
1840  jrr = ledgerEntryState(env, carol, gw, "USD");
1841  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-475");
1842 
1843  auto jro = ledgerEntryOffer(env, carol, carolOfferSeq);
1844  BEAST_EXPECT(
1845  jro[jss::node][jss::TakerGets] ==
1846  USD(25).value().getJson(JsonOptions::none));
1847  BEAST_EXPECT(
1848  jro[jss::node][jss::TakerPays] == XRP(250).value().getText());
1849  }
1850 
1851  void
1853  {
1854  testcase("Cross Currency Payment: End with XRP");
1855 
1856  using namespace jtx;
1857 
1858  Env env{*this, features};
1859 
1860  auto const gw = Account{"gateway"};
1861  auto const alice = Account{"alice"};
1862  auto const bob = Account{"bob"};
1863  auto const carol = Account{"carol"};
1864  auto const USD = gw["USD"];
1865 
1866  env.fund(XRP(10000), gw, alice, bob, carol);
1867 
1868  env(trust(alice, USD(1000)));
1869  env(trust(carol, USD(2000)));
1870 
1871  env(pay(gw, alice, alice["USD"](500)));
1872 
1873  auto const carolOfferSeq = env.seq(carol);
1874  env(offer(carol, USD(50), XRP(500)));
1875 
1876  env(pay(alice, bob, XRP(250)), sendmax(USD(333)));
1877 
1878  auto jrr = ledgerEntryState(env, alice, gw, "USD");
1879  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-475");
1880 
1881  jrr = ledgerEntryState(env, carol, gw, "USD");
1882  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-25");
1883 
1884  jrr = ledgerEntryRoot(env, bob);
1885  BEAST_EXPECT(
1886  jrr[jss::node][sfBalance.fieldName] ==
1888  XRP(10000).value().mantissa() + XRP(250).value().mantissa()));
1889 
1890  auto jro = ledgerEntryOffer(env, carol, carolOfferSeq);
1891  BEAST_EXPECT(
1892  jro[jss::node][jss::TakerGets] == XRP(250).value().getText());
1893  BEAST_EXPECT(
1894  jro[jss::node][jss::TakerPays] ==
1895  USD(25).value().getJson(JsonOptions::none));
1896  }
1897 
1898  void
1900  {
1901  testcase("Cross Currency Payment: Bridged");
1902 
1903  using namespace jtx;
1904 
1905  Env env{*this, features};
1906 
1907  auto const gw1 = Account{"gateway_1"};
1908  auto const gw2 = Account{"gateway_2"};
1909  auto const alice = Account{"alice"};
1910  auto const bob = Account{"bob"};
1911  auto const carol = Account{"carol"};
1912  auto const dan = Account{"dan"};
1913  auto const USD = gw1["USD"];
1914  auto const EUR = gw2["EUR"];
1915 
1916  env.fund(XRP(10000), gw1, gw2, alice, bob, carol, dan);
1917 
1918  env(trust(alice, USD(1000)));
1919  env(trust(bob, EUR(1000)));
1920  env(trust(carol, USD(1000)));
1921  env(trust(dan, EUR(1000)));
1922 
1923  env(pay(gw1, alice, alice["USD"](500)));
1924  env(pay(gw2, dan, dan["EUR"](400)));
1925 
1926  auto const carolOfferSeq = env.seq(carol);
1927  env(offer(carol, USD(50), XRP(500)));
1928 
1929  auto const danOfferSeq = env.seq(dan);
1930  env(offer(dan, XRP(500), EUR(50)));
1931 
1933  jtp[0u][0u][jss::currency] = "XRP";
1934  env(pay(alice, bob, EUR(30)), json(jss::Paths, jtp), sendmax(USD(333)));
1935 
1936  auto jrr = ledgerEntryState(env, alice, gw1, "USD");
1937  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "470");
1938 
1939  jrr = ledgerEntryState(env, bob, gw2, "EUR");
1940  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-30");
1941 
1942  jrr = ledgerEntryState(env, carol, gw1, "USD");
1943  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-30");
1944 
1945  jrr = ledgerEntryState(env, dan, gw2, "EUR");
1946  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-370");
1947 
1948  auto jro = ledgerEntryOffer(env, carol, carolOfferSeq);
1949  BEAST_EXPECT(
1950  jro[jss::node][jss::TakerGets] == XRP(200).value().getText());
1951  BEAST_EXPECT(
1952  jro[jss::node][jss::TakerPays] ==
1953  USD(20).value().getJson(JsonOptions::none));
1954 
1955  jro = ledgerEntryOffer(env, dan, danOfferSeq);
1956  BEAST_EXPECT(
1957  jro[jss::node][jss::TakerGets] ==
1958  gw2["EUR"](20).value().getJson(JsonOptions::none));
1959  BEAST_EXPECT(
1960  jro[jss::node][jss::TakerPays] == XRP(200).value().getText());
1961  }
1962 
1963  void
1965  {
1966  // At least with Taker bridging, a sensitivity was identified if the
1967  // second leg goes dry before the first one. This test exercises that
1968  // case.
1969  testcase("Auto Bridged Second Leg Dry");
1970 
1971  using namespace jtx;
1972  Env env(*this, features);
1973 
1974  Account const alice{"alice"};
1975  Account const bob{"bob"};
1976  Account const carol{"carol"};
1977  Account const gw{"gateway"};
1978  auto const USD = gw["USD"];
1979  auto const EUR = gw["EUR"];
1980 
1981  env.fund(XRP(100000000), alice, bob, carol, gw);
1982 
1983  env.trust(USD(10), alice);
1984  env.close();
1985  env(pay(gw, alice, USD(10)));
1986  env.trust(USD(10), carol);
1987  env.close();
1988  env(pay(gw, carol, USD(3)));
1989 
1990  env(offer(alice, EUR(2), XRP(1)));
1991  env(offer(alice, EUR(2), XRP(1)));
1992 
1993  env(offer(alice, XRP(1), USD(4)));
1994  env(offer(carol, XRP(1), USD(3)));
1995  env.close();
1996 
1997  // Bob offers to buy 10 USD for 10 EUR.
1998  // 1. He spends 2 EUR taking Alice's auto-bridged offers and
1999  // gets 4 USD for that.
2000  // 2. He spends another 2 EUR taking Alice's last EUR->XRP offer and
2001  // Carol's XRP-USD offer. He gets 3 USD for that.
2002  // The key for this test is that Alice's XRP->USD leg goes dry before
2003  // Alice's EUR->XRP. The XRP->USD leg is the second leg which showed
2004  // some sensitivity.
2005  env.trust(EUR(10), bob);
2006  env.close();
2007  env(pay(gw, bob, EUR(10)));
2008  env.close();
2009  env(offer(bob, USD(10), EUR(10)));
2010  env.close();
2011 
2012  env.require(balance(bob, USD(7)));
2013  env.require(balance(bob, EUR(6)));
2014  env.require(offers(bob, 1));
2015  env.require(owners(bob, 3));
2016 
2017  env.require(balance(alice, USD(6)));
2018  env.require(balance(alice, EUR(4)));
2019  env.require(offers(alice, 0));
2020  env.require(owners(alice, 2));
2021 
2022  env.require(balance(carol, USD(0)));
2023  env.require(balance(carol, EUR(none)));
2024  // If neither featureFlowCross nor fixTakerDryOfferRemoval are defined
2025  // then carol's offer will be left on the books, but with zero value.
2026  int const emptyOfferCount{
2027  features[featureFlowCross] || features[fixTakerDryOfferRemoval]
2028  ? 0
2029  : 1};
2030 
2031  env.require(offers(carol, 0 + emptyOfferCount));
2032  env.require(owners(carol, 1 + emptyOfferCount));
2033  }
2034 
2035  void
2037  {
2038  testcase("Offer Fees Consume Funds");
2039 
2040  using namespace jtx;
2041 
2042  Env env{*this, features};
2043 
2044  auto const gw1 = Account{"gateway_1"};
2045  auto const gw2 = Account{"gateway_2"};
2046  auto const gw3 = Account{"gateway_3"};
2047  auto const alice = Account{"alice"};
2048  auto const bob = Account{"bob"};
2049  auto const USD1 = gw1["USD"];
2050  auto const USD2 = gw2["USD"];
2051  auto const USD3 = gw3["USD"];
2052 
2053  // Provide micro amounts to compensate for fees to make results round
2054  // nice.
2055  // reserve: Alice has 3 entries in the ledger, via trust lines
2056  // fees:
2057  // 1 for each trust limit == 3 (alice < mtgox/amazon/bitstamp) +
2058  // 1 for payment == 4
2059  auto const starting_xrp = XRP(100) +
2060  env.current()->fees().accountReserve(3) +
2061  env.current()->fees().base * 4;
2062 
2063  env.fund(starting_xrp, gw1, gw2, gw3, alice, bob);
2064 
2065  env(trust(alice, USD1(1000)));
2066  env(trust(alice, USD2(1000)));
2067  env(trust(alice, USD3(1000)));
2068  env(trust(bob, USD1(1000)));
2069  env(trust(bob, USD2(1000)));
2070 
2071  env(pay(gw1, bob, bob["USD"](500)));
2072 
2073  env(offer(bob, XRP(200), USD1(200)));
2074  // Alice has 350 fees - a reserve of 50 = 250 reserve = 100 available.
2075  // Ask for more than available to prove reserve works.
2076  env(offer(alice, USD1(200), XRP(200)));
2077 
2078  auto jrr = ledgerEntryState(env, alice, gw1, "USD");
2079  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "100");
2080  jrr = ledgerEntryRoot(env, alice);
2081  BEAST_EXPECT(
2082  jrr[jss::node][sfBalance.fieldName] ==
2083  STAmount(env.current()->fees().accountReserve(3)).getText());
2084 
2085  jrr = ledgerEntryState(env, bob, gw1, "USD");
2086  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400");
2087  }
2088 
2089  void
2091  {
2092  testcase("Offer Create, then Cross");
2093 
2094  using namespace jtx;
2095 
2096  for (auto NumberSwitchOver : {false, true})
2097  {
2098  Env env{*this, features};
2099  if (NumberSwitchOver)
2100  env.enableFeature(fixUniversalNumber);
2101  else
2102  env.disableFeature(fixUniversalNumber);
2103 
2104  auto const gw = Account{"gateway"};
2105  auto const alice = Account{"alice"};
2106  auto const bob = Account{"bob"};
2107  auto const USD = gw["USD"];
2108 
2109  env.fund(XRP(10000), gw, alice, bob);
2110 
2111  env(rate(gw, 1.005));
2112 
2113  env(trust(alice, USD(1000)));
2114  env(trust(bob, USD(1000)));
2115  env(trust(gw, alice["USD"](50)));
2116 
2117  env(pay(gw, bob, bob["USD"](1)));
2118  env(pay(alice, gw, USD(50)));
2119 
2120  env(trust(gw, alice["USD"](0)));
2121 
2122  env(offer(alice, USD(50), XRP(150000)));
2123  env(offer(bob, XRP(100), USD(0.1)));
2124 
2125  auto jrr = ledgerEntryState(env, alice, gw, "USD");
2126  BEAST_EXPECT(
2127  jrr[jss::node][sfBalance.fieldName][jss::value] ==
2128  "49.96666666666667");
2129 
2130  jrr = ledgerEntryState(env, bob, gw, "USD");
2131  Json::Value const bobsUSD =
2132  jrr[jss::node][sfBalance.fieldName][jss::value];
2133  if (!NumberSwitchOver)
2134  {
2135  BEAST_EXPECT(bobsUSD == "-0.966500000033334");
2136  }
2137  else
2138  {
2139  BEAST_EXPECT(bobsUSD == "-0.9665000000333333");
2140  }
2141  }
2142  }
2143 
2144  void
2146  {
2147  testcase("Offer tfSell: Basic Sell");
2148 
2149  using namespace jtx;
2150 
2151  Env env{*this, features};
2152 
2153  auto const gw = Account{"gateway"};
2154  auto const alice = Account{"alice"};
2155  auto const bob = Account{"bob"};
2156  auto const USD = gw["USD"];
2157 
2158  auto const starting_xrp = XRP(100) +
2159  env.current()->fees().accountReserve(1) +
2160  env.current()->fees().base * 2;
2161 
2162  env.fund(starting_xrp, gw, alice, bob);
2163 
2164  env(trust(alice, USD(1000)));
2165  env(trust(bob, USD(1000)));
2166 
2167  env(pay(gw, bob, bob["USD"](500)));
2168 
2169  env(offer(bob, XRP(200), USD(200)), json(jss::Flags, tfSell));
2170  // Alice has 350 + fees - a reserve of 50 = 250 reserve = 100 available.
2171  // Alice has 350 + fees - a reserve of 50 = 250 reserve = 100 available.
2172  // Ask for more than available to prove reserve works.
2173  env(offer(alice, USD(200), XRP(200)), json(jss::Flags, tfSell));
2174 
2175  auto jrr = ledgerEntryState(env, alice, gw, "USD");
2176  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
2177  jrr = ledgerEntryRoot(env, alice);
2178  BEAST_EXPECT(
2179  jrr[jss::node][sfBalance.fieldName] ==
2180  STAmount(env.current()->fees().accountReserve(1)).getText());
2181 
2182  jrr = ledgerEntryState(env, bob, gw, "USD");
2183  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400");
2184  }
2185 
2186  void
2188  {
2189  testcase("Offer tfSell: 2x Sell Exceed Limit");
2190 
2191  using namespace jtx;
2192 
2193  Env env{*this, features};
2194 
2195  auto const gw = Account{"gateway"};
2196  auto const alice = Account{"alice"};
2197  auto const bob = Account{"bob"};
2198  auto const USD = gw["USD"];
2199 
2200  auto const starting_xrp = XRP(100) +
2201  env.current()->fees().accountReserve(1) +
2202  env.current()->fees().base * 2;
2203 
2204  env.fund(starting_xrp, gw, alice, bob);
2205 
2206  env(trust(alice, USD(150)));
2207  env(trust(bob, USD(1000)));
2208 
2209  env(pay(gw, bob, bob["USD"](500)));
2210 
2211  env(offer(bob, XRP(100), USD(200)));
2212  // Alice has 350 fees - a reserve of 50 = 250 reserve = 100 available.
2213  // Ask for more than available to prove reserve works.
2214  // Taker pays 100 USD for 100 XRP.
2215  // Selling XRP.
2216  // Will sell all 100 XRP and get more USD than asked for.
2217  env(offer(alice, USD(100), XRP(100)), json(jss::Flags, tfSell));
2218 
2219  auto jrr = ledgerEntryState(env, alice, gw, "USD");
2220  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-200");
2221  jrr = ledgerEntryRoot(env, alice);
2222  BEAST_EXPECT(
2223  jrr[jss::node][sfBalance.fieldName] ==
2224  STAmount(env.current()->fees().accountReserve(1)).getText());
2225 
2226  jrr = ledgerEntryState(env, bob, gw, "USD");
2227  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-300");
2228  }
2229 
2230  void
2232  {
2233  testcase("Client Issue #535: Gateway Cross Currency");
2234 
2235  using namespace jtx;
2236 
2237  Env env{*this, features};
2238 
2239  auto const gw = Account{"gateway"};
2240  auto const alice = Account{"alice"};
2241  auto const bob = Account{"bob"};
2242  auto const XTS = gw["XTS"];
2243  auto const XXX = gw["XXX"];
2244 
2245  auto const starting_xrp = XRP(100.1) +
2246  env.current()->fees().accountReserve(1) +
2247  env.current()->fees().base * 2;
2248 
2249  env.fund(starting_xrp, gw, alice, bob);
2250 
2251  env(trust(alice, XTS(1000)));
2252  env(trust(alice, XXX(1000)));
2253  env(trust(bob, XTS(1000)));
2254  env(trust(bob, XXX(1000)));
2255 
2256  env(pay(gw, alice, alice["XTS"](100)));
2257  env(pay(gw, alice, alice["XXX"](100)));
2258  env(pay(gw, bob, bob["XTS"](100)));
2259  env(pay(gw, bob, bob["XXX"](100)));
2260 
2261  env(offer(alice, XTS(100), XXX(100)));
2262 
2263  // WS client is used here because the RPC client could not
2264  // be convinced to pass the build_path argument
2265  auto wsc = makeWSClient(env.app().config());
2266  Json::Value payment;
2267  payment[jss::secret] = toBase58(generateSeed("bob"));
2268  payment[jss::id] = env.seq(bob);
2269  payment[jss::build_path] = true;
2270  payment[jss::tx_json] = pay(bob, bob, bob["XXX"](1));
2271  payment[jss::tx_json][jss::Sequence] =
2272  env.current()
2273  ->read(keylet::account(bob.id()))
2274  ->getFieldU32(sfSequence);
2275  payment[jss::tx_json][jss::Fee] = to_string(env.current()->fees().base);
2276  payment[jss::tx_json][jss::SendMax] =
2277  bob["XTS"](1.5).value().getJson(JsonOptions::none);
2278  auto jrr = wsc->invoke("submit", payment);
2279  BEAST_EXPECT(jrr[jss::status] == "success");
2280  BEAST_EXPECT(jrr[jss::result][jss::engine_result] == "tesSUCCESS");
2281  if (wsc->version() == 2)
2282  {
2283  BEAST_EXPECT(
2284  jrr.isMember(jss::jsonrpc) && jrr[jss::jsonrpc] == "2.0");
2285  BEAST_EXPECT(
2286  jrr.isMember(jss::ripplerpc) && jrr[jss::ripplerpc] == "2.0");
2287  BEAST_EXPECT(jrr.isMember(jss::id) && jrr[jss::id] == 5);
2288  }
2289 
2290  jrr = ledgerEntryState(env, alice, gw, "XTS");
2291  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101");
2292  jrr = ledgerEntryState(env, alice, gw, "XXX");
2293  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-99");
2294 
2295  jrr = ledgerEntryState(env, bob, gw, "XTS");
2296  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-99");
2297  jrr = ledgerEntryState(env, bob, gw, "XXX");
2298  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101");
2299  }
2300 
2301  // Helper function that validates a *defaulted* trustline: one that has
2302  // no unusual flags set and doesn't have high or low limits set. Such a
2303  // trustline may have an actual balance (it can be created automatically
2304  // if a user places an offer to acquire an IOU for which they don't have
2305  // a trust line defined). If the trustline is not defaulted then the tests
2306  // will not pass.
2307  void
2309  jtx::Env& env,
2310  jtx::Account const& account,
2311  jtx::PrettyAmount const& expectBalance)
2312  {
2313  auto const sleTrust =
2314  env.le(keylet::line(account.id(), expectBalance.value().issue()));
2315  BEAST_EXPECT(sleTrust);
2316  if (sleTrust)
2317  {
2318  Issue const issue = expectBalance.value().issue();
2319  bool const accountLow = account.id() < issue.account;
2320 
2321  STAmount low{issue};
2322  STAmount high{issue};
2323 
2324  low.setIssuer(accountLow ? account.id() : issue.account);
2325  high.setIssuer(accountLow ? issue.account : account.id());
2326 
2327  BEAST_EXPECT(sleTrust->getFieldAmount(sfLowLimit) == low);
2328  BEAST_EXPECT(sleTrust->getFieldAmount(sfHighLimit) == high);
2329 
2330  STAmount actualBalance{sleTrust->getFieldAmount(sfBalance)};
2331  if (!accountLow)
2332  actualBalance.negate();
2333 
2334  BEAST_EXPECT(actualBalance == expectBalance);
2335  }
2336  }
2337 
2338  void
2340  {
2341  // Test a number of different corner cases regarding adding a
2342  // possibly crossable offer to an account. The test is table
2343  // driven so it should be easy to add or remove tests.
2344  testcase("Partial Crossing");
2345 
2346  using namespace jtx;
2347 
2348  auto const gw = Account("gateway");
2349  auto const USD = gw["USD"];
2350 
2351  Env env{*this, features};
2352 
2353  env.fund(XRP(10000000), gw);
2354 
2355  // The fee that's charged for transactions
2356  auto const f = env.current()->fees().base;
2357 
2358  // To keep things simple all offers are 1 : 1 for XRP : USD.
2359  enum preTrustType { noPreTrust, gwPreTrust, acctPreTrust };
2360  struct TestData
2361  {
2362  std::string account; // Account operated on
2363  STAmount fundXrp; // Account funded with
2364  int bookAmount; // USD -> XRP offer on the books
2365  preTrustType preTrust; // If true, pre-establish trust line
2366  int offerAmount; // Account offers this much XRP -> USD
2367  TER tec; // Returned tec code
2368  STAmount spentXrp; // Amount removed from fundXrp
2369  PrettyAmount balanceUsd; // Balance on account end
2370  int offers; // Offers on account
2371  int owners; // Owners on account
2372  };
2373 
2374  // clang-format off
2375  TestData const tests[]{
2376  // acct fundXrp bookAmt preTrust offerAmt tec spentXrp balanceUSD offers owners
2377  {"ann", reserve(env, 0) + 0 * f, 1, noPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0}, // Account is at the reserve, and will dip below once fees are subtracted.
2378  {"bev", reserve(env, 0) + 1 * f, 1, noPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0}, // Account has just enough for the reserve and the fee.
2379  {"cam", reserve(env, 0) + 2 * f, 0, noPreTrust, 1000, tecINSUF_RESERVE_OFFER, f, USD( 0), 0, 0}, // Account has enough for the reserve, the fee and the offer, and a bit more, but not enough for the reserve after the offer is placed.
2380  {"deb", reserve(env, 0) + 2 * f, 1, noPreTrust, 1000, tesSUCCESS, 2 * f, USD(0.00001), 0, 1}, // Account has enough to buy a little USD then the offer runs dry.
2381  {"eve", reserve(env, 1) + 0 * f, 0, noPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1}, // No offer to cross
2382  {"flo", reserve(env, 1) + 0 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 0, 1},
2383  {"gay", reserve(env, 1) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 50) + f, USD( 50), 0, 1},
2384  {"hye", XRP(1000) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 800) + f, USD( 800), 0, 1},
2385  {"ivy", XRP( 1) + reserve(env, 1) + 1 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 0, 1},
2386  {"joy", XRP( 1) + reserve(env, 2) + 1 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 2},
2387  {"kim", XRP( 900) + reserve(env, 2) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
2388  {"liz", XRP( 998) + reserve(env, 0) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 998) + f, USD( 998), 0, 1},
2389  {"meg", XRP( 998) + reserve(env, 1) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
2390  {"nia", XRP( 998) + reserve(env, 2) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 1, 2},
2391  {"ova", XRP( 999) + reserve(env, 0) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
2392  {"pam", XRP( 999) + reserve(env, 1) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP(1000) + f, USD( 1000), 0, 1},
2393  {"rae", XRP( 999) + reserve(env, 2) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP(1000) + f, USD( 1000), 0, 1},
2394  {"sue", XRP(1000) + reserve(env, 2) + 1 * f, 0, noPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1},
2395  //---------------- Pre-established trust lines ---------------------
2396  {"abe", reserve(env, 0) + 0 * f, 1, gwPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0},
2397  {"bud", reserve(env, 0) + 1 * f, 1, gwPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0},
2398  {"che", reserve(env, 0) + 2 * f, 0, gwPreTrust, 1000, tecINSUF_RESERVE_OFFER, f, USD( 0), 0, 0},
2399  {"dan", reserve(env, 0) + 2 * f, 1, gwPreTrust, 1000, tesSUCCESS, 2 * f, USD(0.00001), 0, 0},
2400  {"eli", XRP( 20) + reserve(env, 0) + 1 * f, 1000, gwPreTrust, 1000, tesSUCCESS, XRP(20) + 1 * f, USD( 20), 0, 0},
2401  {"fyn", reserve(env, 1) + 0 * f, 0, gwPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1},
2402  {"gar", reserve(env, 1) + 0 * f, 1, gwPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 1},
2403  {"hal", reserve(env, 1) + 1 * f, 1, gwPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 1},
2404 
2405  {"ned", reserve(env, 1) + 0 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
2406  {"ole", reserve(env, 1) + 1 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
2407  {"pat", reserve(env, 1) + 2 * f, 0, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
2408  {"quy", reserve(env, 1) + 2 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
2409  {"ron", reserve(env, 1) + 3 * f, 0, acctPreTrust, 1000, tecINSUF_RESERVE_OFFER, 2 * f, USD( 0), 0, 1},
2410  {"syd", reserve(env, 1) + 3 * f, 1, acctPreTrust, 1000, tesSUCCESS, 3 * f, USD(0.00001), 0, 1},
2411  {"ted", XRP( 20) + reserve(env, 1) + 2 * f, 1000, acctPreTrust, 1000, tesSUCCESS, XRP(20) + 2 * f, USD( 20), 0, 1},
2412  {"uli", reserve(env, 2) + 0 * f, 0, acctPreTrust, 1000, tecINSUF_RESERVE_OFFER, 2 * f, USD( 0), 0, 1},
2413  {"vic", reserve(env, 2) + 0 * f, 1, acctPreTrust, 1000, tesSUCCESS, XRP( 1) + 2 * f, USD( 1), 0, 1},
2414  {"wes", reserve(env, 2) + 1 * f, 0, acctPreTrust, 1000, tesSUCCESS, 2 * f, USD( 0), 1, 2},
2415  {"xan", reserve(env, 2) + 1 * f, 1, acctPreTrust, 1000, tesSUCCESS, XRP( 1) + 2 * f, USD( 1), 1, 2},
2416  };
2417  // clang-format on
2418 
2419  for (auto const& t : tests)
2420  {
2421  auto const acct = Account(t.account);
2422  env.fund(t.fundXrp, acct);
2423  env.close();
2424 
2425  // Make sure gateway has no current offers.
2426  env.require(offers(gw, 0));
2427 
2428  // The gateway optionally creates an offer that would be crossed.
2429  auto const book = t.bookAmount;
2430  if (book)
2431  env(offer(gw, XRP(book), USD(book)));
2432  env.close();
2433  std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
2434 
2435  // Optionally pre-establish a trustline between gw and acct.
2436  if (t.preTrust == gwPreTrust)
2437  env(trust(gw, acct["USD"](1)));
2438 
2439  // Optionally pre-establish a trustline between acct and gw.
2440  // Note this is not really part of the test, so we expect there
2441  // to be enough XRP reserve for acct to create the trust line.
2442  if (t.preTrust == acctPreTrust)
2443  env(trust(acct, USD(1)));
2444 
2445  env.close();
2446 
2447  {
2448  // Acct creates an offer. This is the heart of the test.
2449  auto const acctOffer = t.offerAmount;
2450  env(offer(acct, USD(acctOffer), XRP(acctOffer)), ter(t.tec));
2451  env.close();
2452  }
2453  std::uint32_t const acctOfferSeq = env.seq(acct) - 1;
2454 
2455  BEAST_EXPECT(env.balance(acct, USD.issue()) == t.balanceUsd);
2456  BEAST_EXPECT(
2457  env.balance(acct, xrpIssue()) == t.fundXrp - t.spentXrp);
2458  env.require(offers(acct, t.offers));
2459  env.require(owners(acct, t.owners));
2460 
2461  auto acctOffers = offersOnAccount(env, acct);
2462  BEAST_EXPECT(acctOffers.size() == t.offers);
2463  if (acctOffers.size() && t.offers)
2464  {
2465  auto const& acctOffer = *(acctOffers.front());
2466 
2467  auto const leftover = t.offerAmount - t.bookAmount;
2468  BEAST_EXPECT(acctOffer[sfTakerGets] == XRP(leftover));
2469  BEAST_EXPECT(acctOffer[sfTakerPays] == USD(leftover));
2470  }
2471 
2472  if (t.preTrust == noPreTrust)
2473  {
2474  if (t.balanceUsd.value().signum())
2475  {
2476  // Verify the correct contents of the trustline
2477  verifyDefaultTrustline(env, acct, t.balanceUsd);
2478  }
2479  else
2480  {
2481  // Verify that no trustline was created.
2482  auto const sleTrust =
2483  env.le(keylet::line(acct, USD.issue()));
2484  BEAST_EXPECT(!sleTrust);
2485  }
2486  }
2487 
2488  // Give the next loop a clean slate by canceling any left-overs
2489  // in the offers.
2490  env(offer_cancel(acct, acctOfferSeq));
2491  env(offer_cancel(gw, gwOfferSeq));
2492  env.close();
2493  }
2494  }
2495 
2496  void
2498  {
2499  testcase("XRP Direct Crossing");
2500 
2501  using namespace jtx;
2502 
2503  auto const gw = Account("gateway");
2504  auto const alice = Account("alice");
2505  auto const bob = Account("bob");
2506  auto const USD = gw["USD"];
2507 
2508  auto const usdOffer = USD(1000);
2509  auto const xrpOffer = XRP(1000);
2510 
2511  Env env{*this, features};
2512 
2513  env.fund(XRP(1000000), gw, bob);
2514  env.close();
2515 
2516  // The fee that's charged for transactions.
2517  auto const fee = env.current()->fees().base;
2518 
2519  // alice's account has enough for the reserve, one trust line plus two
2520  // offers, and two fees.
2521  env.fund(reserve(env, 2) + fee * 2, alice);
2522  env.close();
2523 
2524  env(trust(alice, usdOffer));
2525 
2526  env.close();
2527 
2528  env(pay(gw, alice, usdOffer));
2529  env.close();
2530  env.require(balance(alice, usdOffer), offers(alice, 0), offers(bob, 0));
2531 
2532  // The scenario:
2533  // o alice has USD but wants XRP.
2534  // o bob has XRP but wants USD.
2535  auto const alicesXRP = env.balance(alice);
2536  auto const bobsXRP = env.balance(bob);
2537 
2538  env(offer(alice, xrpOffer, usdOffer));
2539  env.close();
2540  env(offer(bob, usdOffer, xrpOffer));
2541 
2542  env.close();
2543  env.require(
2544  balance(alice, USD(0)),
2545  balance(bob, usdOffer),
2546  balance(alice, alicesXRP + xrpOffer - fee),
2547  balance(bob, bobsXRP - xrpOffer - fee),
2548  offers(alice, 0),
2549  offers(bob, 0));
2550 
2551  verifyDefaultTrustline(env, bob, usdOffer);
2552 
2553  // Make two more offers that leave one of the offers non-dry.
2554  env(offer(alice, USD(999), XRP(999)));
2555  env(offer(bob, xrpOffer, usdOffer));
2556 
2557  env.close();
2558  env.require(balance(alice, USD(999)));
2559  env.require(balance(bob, USD(1)));
2560  env.require(offers(alice, 0));
2561  verifyDefaultTrustline(env, bob, USD(1));
2562  {
2563  auto const bobsOffers = offersOnAccount(env, bob);
2564  BEAST_EXPECT(bobsOffers.size() == 1);
2565  auto const& bobsOffer = *(bobsOffers.front());
2566 
2567  BEAST_EXPECT(bobsOffer[sfLedgerEntryType] == ltOFFER);
2568  BEAST_EXPECT(bobsOffer[sfTakerGets] == USD(1));
2569  BEAST_EXPECT(bobsOffer[sfTakerPays] == XRP(1));
2570  }
2571  }
2572 
2573  void
2575  {
2576  testcase("Direct Crossing");
2577 
2578  using namespace jtx;
2579 
2580  auto const gw = Account("gateway");
2581  auto const alice = Account("alice");
2582  auto const bob = Account("bob");
2583  auto const USD = gw["USD"];
2584  auto const EUR = gw["EUR"];
2585 
2586  auto const usdOffer = USD(1000);
2587  auto const eurOffer = EUR(1000);
2588 
2589  Env env{*this, features};
2590 
2591  env.fund(XRP(1000000), gw);
2592  env.close();
2593 
2594  // The fee that's charged for transactions.
2595  auto const fee = env.current()->fees().base;
2596 
2597  // Each account has enough for the reserve, two trust lines, one
2598  // offer, and two fees.
2599  env.fund(reserve(env, 3) + fee * 3, alice);
2600  env.fund(reserve(env, 3) + fee * 2, bob);
2601  env.close();
2602 
2603  env(trust(alice, usdOffer));
2604  env(trust(bob, eurOffer));
2605  env.close();
2606 
2607  env(pay(gw, alice, usdOffer));
2608  env(pay(gw, bob, eurOffer));
2609  env.close();
2610 
2611  env.require(balance(alice, usdOffer), balance(bob, eurOffer));
2612 
2613  // The scenario:
2614  // o alice has USD but wants EUR.
2615  // o bob has EUR but wants USD.
2616  env(offer(alice, eurOffer, usdOffer));
2617  env(offer(bob, usdOffer, eurOffer));
2618 
2619  env.close();
2620  env.require(
2621  balance(alice, eurOffer),
2622  balance(bob, usdOffer),
2623  offers(alice, 0),
2624  offers(bob, 0));
2625 
2626  // Alice's offer crossing created a default EUR trustline and
2627  // Bob's offer crossing created a default USD trustline:
2628  verifyDefaultTrustline(env, alice, eurOffer);
2629  verifyDefaultTrustline(env, bob, usdOffer);
2630 
2631  // Make two more offers that leave one of the offers non-dry.
2632  // Guarantee the order of application by putting a close()
2633  // between them.
2634  env(offer(bob, eurOffer, usdOffer));
2635  env.close();
2636 
2637  env(offer(alice, USD(999), eurOffer));
2638  env.close();
2639 
2640  env.require(offers(alice, 0));
2641  env.require(offers(bob, 1));
2642 
2643  env.require(balance(alice, USD(999)));
2644  env.require(balance(alice, EUR(1)));
2645  env.require(balance(bob, USD(1)));
2646  env.require(balance(bob, EUR(999)));
2647 
2648  {
2649  auto bobsOffers = offersOnAccount(env, bob);
2650  if (BEAST_EXPECT(bobsOffers.size() == 1))
2651  {
2652  auto const& bobsOffer = *(bobsOffers.front());
2653 
2654  BEAST_EXPECT(bobsOffer[sfTakerGets] == USD(1));
2655  BEAST_EXPECT(bobsOffer[sfTakerPays] == EUR(1));
2656  }
2657  }
2658 
2659  // alice makes one more offer that cleans out bob's offer.
2660  env(offer(alice, USD(1), EUR(1)));
2661  env.close();
2662 
2663  env.require(balance(alice, USD(1000)));
2664  env.require(balance(alice, EUR(none)));
2665  env.require(balance(bob, USD(none)));
2666  env.require(balance(bob, EUR(1000)));
2667  env.require(offers(alice, 0));
2668  env.require(offers(bob, 0));
2669 
2670  // The two trustlines that were generated by offers should be gone.
2671  BEAST_EXPECT(!env.le(keylet::line(alice.id(), EUR.issue())));
2672  BEAST_EXPECT(!env.le(keylet::line(bob.id(), USD.issue())));
2673 
2674  // Make two more offers that leave one of the offers non-dry. We
2675  // need to properly sequence the transactions:
2676  env(offer(alice, EUR(999), usdOffer));
2677  env.close();
2678 
2679  env(offer(bob, usdOffer, eurOffer));
2680  env.close();
2681 
2682  env.require(offers(alice, 0));
2683  env.require(offers(bob, 0));
2684 
2685  env.require(balance(alice, USD(0)));
2686  env.require(balance(alice, EUR(999)));
2687  env.require(balance(bob, USD(1000)));
2688  env.require(balance(bob, EUR(1)));
2689  }
2690 
2691  void
2693  {
2694  testcase("Bridged Crossing");
2695 
2696  using namespace jtx;
2697 
2698  auto const gw = Account("gateway");
2699  auto const alice = Account("alice");
2700  auto const bob = Account("bob");
2701  auto const carol = Account("carol");
2702  auto const USD = gw["USD"];
2703  auto const EUR = gw["EUR"];
2704 
2705  auto const usdOffer = USD(1000);
2706  auto const eurOffer = EUR(1000);
2707 
2708  Env env{*this, features};
2709 
2710  env.fund(XRP(1000000), gw, alice, bob, carol);
2711  env.close();
2712 
2713  env(trust(alice, usdOffer));
2714  env(trust(carol, eurOffer));
2715  env.close();
2716  env(pay(gw, alice, usdOffer));
2717  env(pay(gw, carol, eurOffer));
2718  env.close();
2719 
2720  // The scenario:
2721  // o alice has USD but wants XPR.
2722  // o bob has XRP but wants EUR.
2723  // o carol has EUR but wants USD.
2724  // Note that carol's offer must come last. If carol's offer is placed
2725  // before bob's or alice's, then autobridging will not occur.
2726  env(offer(alice, XRP(1000), usdOffer));
2727  env(offer(bob, eurOffer, XRP(1000)));
2728  auto const bobXrpBalance = env.balance(bob);
2729  env.close();
2730 
2731  // carol makes an offer that partially consumes alice and bob's offers.
2732  env(offer(carol, USD(400), EUR(400)));
2733  env.close();
2734 
2735  env.require(
2736  balance(alice, USD(600)),
2737  balance(bob, EUR(400)),
2738  balance(carol, USD(400)),
2739  balance(bob, bobXrpBalance - XRP(400)),
2740  offers(carol, 0));
2741  verifyDefaultTrustline(env, bob, EUR(400));
2742  verifyDefaultTrustline(env, carol, USD(400));
2743  {
2744  auto const alicesOffers = offersOnAccount(env, alice);
2745  BEAST_EXPECT(alicesOffers.size() == 1);
2746  auto const& alicesOffer = *(alicesOffers.front());
2747 
2748  BEAST_EXPECT(alicesOffer[sfLedgerEntryType] == ltOFFER);
2749  BEAST_EXPECT(alicesOffer[sfTakerGets] == USD(600));
2750  BEAST_EXPECT(alicesOffer[sfTakerPays] == XRP(600));
2751  }
2752  {
2753  auto const bobsOffers = offersOnAccount(env, bob);
2754  BEAST_EXPECT(bobsOffers.size() == 1);
2755  auto const& bobsOffer = *(bobsOffers.front());
2756 
2757  BEAST_EXPECT(bobsOffer[sfLedgerEntryType] == ltOFFER);
2758  BEAST_EXPECT(bobsOffer[sfTakerGets] == XRP(600));
2759  BEAST_EXPECT(bobsOffer[sfTakerPays] == EUR(600));
2760  }
2761 
2762  // carol makes an offer that exactly consumes alice and bob's offers.
2763  env(offer(carol, USD(600), EUR(600)));
2764  env.close();
2765 
2766  env.require(
2767  balance(alice, USD(0)),
2768  balance(bob, eurOffer),
2769  balance(carol, usdOffer),
2770  balance(bob, bobXrpBalance - XRP(1000)),
2771  offers(bob, 0),
2772  offers(carol, 0));
2773  verifyDefaultTrustline(env, bob, EUR(1000));
2774  verifyDefaultTrustline(env, carol, USD(1000));
2775 
2776  // In pre-flow code alice's offer is left empty in the ledger.
2777  auto const alicesOffers = offersOnAccount(env, alice);
2778  if (alicesOffers.size() != 0)
2779  {
2780  BEAST_EXPECT(alicesOffers.size() == 1);
2781  auto const& alicesOffer = *(alicesOffers.front());
2782 
2783  BEAST_EXPECT(alicesOffer[sfLedgerEntryType] == ltOFFER);
2784  BEAST_EXPECT(alicesOffer[sfTakerGets] == USD(0));
2785  BEAST_EXPECT(alicesOffer[sfTakerPays] == XRP(0));
2786  }
2787  }
2788 
2789  void
2791  {
2792  // Test a number of different corner cases regarding offer crossing
2793  // when the tfSell flag is set. The test is table driven so it
2794  // should be easy to add or remove tests.
2795  testcase("Sell Offer");
2796 
2797  using namespace jtx;
2798 
2799  auto const gw = Account("gateway");
2800  auto const USD = gw["USD"];
2801 
2802  Env env{*this, features};
2803 
2804  env.fund(XRP(10000000), gw);
2805 
2806  // The fee that's charged for transactions
2807  auto const f = env.current()->fees().base;
2808 
2809  // To keep things simple all offers are 1 : 1 for XRP : USD.
2810  enum preTrustType { noPreTrust, gwPreTrust, acctPreTrust };
2811  struct TestData
2812  {
2813  std::string account; // Account operated on
2814  STAmount fundXrp; // XRP acct funded with
2815  STAmount fundUSD; // USD acct funded with
2816  STAmount gwGets; // gw's offer
2817  STAmount gwPays; //
2818  STAmount acctGets; // acct's offer
2819  STAmount acctPays; //
2820  TER tec; // Returned tec code
2821  STAmount spentXrp; // Amount removed from fundXrp
2822  STAmount finalUsd; // Final USD balance on acct
2823  int offers; // Offers on acct
2824  int owners; // Owners on acct
2825  STAmount takerGets; // Remainder of acct's offer
2826  STAmount takerPays; //
2827 
2828  // Constructor with takerGets/takerPays
2829  TestData(
2830  std::string&& account_, // Account operated on
2831  STAmount const& fundXrp_, // XRP acct funded with
2832  STAmount const& fundUSD_, // USD acct funded with
2833  STAmount const& gwGets_, // gw's offer
2834  STAmount const& gwPays_, //
2835  STAmount const& acctGets_, // acct's offer
2836  STAmount const& acctPays_, //
2837  TER tec_, // Returned tec code
2838  STAmount const& spentXrp_, // Amount removed from fundXrp
2839  STAmount const& finalUsd_, // Final USD balance on acct
2840  int offers_, // Offers on acct
2841  int owners_, // Owners on acct
2842  STAmount const& takerGets_, // Remainder of acct's offer
2843  STAmount const& takerPays_) //
2844  : account(std::move(account_))
2845  , fundXrp(fundXrp_)
2846  , fundUSD(fundUSD_)
2847  , gwGets(gwGets_)
2848  , gwPays(gwPays_)
2849  , acctGets(acctGets_)
2850  , acctPays(acctPays_)
2851  , tec(tec_)
2852  , spentXrp(spentXrp_)
2853  , finalUsd(finalUsd_)
2854  , offers(offers_)
2855  , owners(owners_)
2856  , takerGets(takerGets_)
2857  , takerPays(takerPays_)
2858  {
2859  }
2860 
2861  // Constructor without takerGets/takerPays
2862  TestData(
2863  std::string&& account_, // Account operated on
2864  STAmount const& fundXrp_, // XRP acct funded with
2865  STAmount const& fundUSD_, // USD acct funded with
2866  STAmount const& gwGets_, // gw's offer
2867  STAmount const& gwPays_, //
2868  STAmount const& acctGets_, // acct's offer
2869  STAmount const& acctPays_, //
2870  TER tec_, // Returned tec code
2871  STAmount const& spentXrp_, // Amount removed from fundXrp
2872  STAmount const& finalUsd_, // Final USD balance on acct
2873  int offers_, // Offers on acct
2874  int owners_) // Owners on acct
2875  : TestData(
2876  std::move(account_),
2877  fundXrp_,
2878  fundUSD_,
2879  gwGets_,
2880  gwPays_,
2881  acctGets_,
2882  acctPays_,
2883  tec_,
2884  spentXrp_,
2885  finalUsd_,
2886  offers_,
2887  owners_,
2888  STAmount{0},
2889  STAmount{0})
2890  {
2891  }
2892  };
2893 
2894  // clang-format off
2895  TestData const tests[]{
2896  // acct pays XRP
2897  // acct fundXrp fundUSD gwGets gwPays acctGets acctPays tec spentXrp finalUSD offers owners takerGets takerPays
2898  {"ann", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD( 5), USD(10), XRP(10), tecINSUF_RESERVE_OFFER, XRP( 0) + (1 * f), USD( 0), 0, 0},
2899  {"bev", XRP(10) + reserve(env, 1) + 1 * f, USD( 0), XRP(10), USD( 5), USD(10), XRP(10), tesSUCCESS, XRP( 0) + (1 * f), USD( 0), 1, 1, XRP(10), USD(10)},
2900  {"cam", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(10), USD(10), XRP(10), tesSUCCESS, XRP( 10) + (1 * f), USD(10), 0, 1},
2901  {"deb", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(20), USD(10), XRP(10), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 0, 1},
2902  {"eve", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(20), USD( 5), XRP( 5), tesSUCCESS, XRP( 5) + (1 * f), USD(10), 0, 1},
2903  {"flo", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(20), USD(20), XRP(20), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 0, 1},
2904  {"gay", XRP(20) + reserve(env, 1) + 1 * f, USD( 0), XRP(10), USD(20), USD(20), XRP(20), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 0, 1},
2905  {"hye", XRP(20) + reserve(env, 2) + 1 * f, USD( 0), XRP(10), USD(20), USD(20), XRP(20), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 1, 2, XRP(10), USD(10)},
2906  // acct pays USD
2907  {"meg", reserve(env, 1) + 2 * f, USD(10), USD(10), XRP( 5), XRP(10), USD(10), tecINSUF_RESERVE_OFFER, XRP( 0) + (2 * f), USD(10), 0, 1},
2908  {"nia", reserve(env, 2) + 2 * f, USD(10), USD(10), XRP( 5), XRP(10), USD(10), tesSUCCESS, XRP( 0) + (2 * f), USD(10), 1, 2, USD(10), XRP(10)},
2909  {"ova", reserve(env, 1) + 2 * f, USD(10), USD(10), XRP(10), XRP(10), USD(10), tesSUCCESS, XRP(-10) + (2 * f), USD( 0), 0, 1},
2910  {"pam", reserve(env, 1) + 2 * f, USD(10), USD(10), XRP(20), XRP(10), USD(10), tesSUCCESS, XRP(-20) + (2 * f), USD( 0), 0, 1},
2911  {"qui", reserve(env, 1) + 2 * f, USD(10), USD(20), XRP(40), XRP(10), USD(10), tesSUCCESS, XRP(-20) + (2 * f), USD( 0), 0, 1},
2912  {"rae", reserve(env, 2) + 2 * f, USD(10), USD( 5), XRP( 5), XRP(10), USD(10), tesSUCCESS, XRP( -5) + (2 * f), USD( 5), 1, 2, USD( 5), XRP( 5)},
2913  {"sue", reserve(env, 2) + 2 * f, USD(10), USD( 5), XRP(10), XRP(10), USD(10), tesSUCCESS, XRP(-10) + (2 * f), USD( 5), 1, 2, USD( 5), XRP( 5)},
2914  };
2915  // clang-format on
2916 
2917  auto const zeroUsd = USD(0);
2918  for (auto const& t : tests)
2919  {
2920  // Make sure gateway has no current offers.
2921  env.require(offers(gw, 0));
2922 
2923  auto const acct = Account(t.account);
2924 
2925  env.fund(t.fundXrp, acct);
2926  env.close();
2927 
2928  // Optionally give acct some USD. This is not part of the test,
2929  // so we assume that acct has sufficient USD to cover the reserve
2930  // on the trust line.
2931  if (t.fundUSD != zeroUsd)
2932  {
2933  env(trust(acct, t.fundUSD));
2934  env.close();
2935  env(pay(gw, acct, t.fundUSD));
2936  env.close();
2937  }
2938 
2939  env(offer(gw, t.gwGets, t.gwPays));
2940  env.close();
2941  std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
2942 
2943  // Acct creates a tfSell offer. This is the heart of the test.
2944  env(offer(acct, t.acctGets, t.acctPays, tfSell), ter(t.tec));
2945  env.close();
2946  std::uint32_t const acctOfferSeq = env.seq(acct) - 1;
2947 
2948  // Check results
2949  BEAST_EXPECT(env.balance(acct, USD.issue()) == t.finalUsd);
2950  BEAST_EXPECT(
2951  env.balance(acct, xrpIssue()) == t.fundXrp - t.spentXrp);
2952  env.require(offers(acct, t.offers));
2953  env.require(owners(acct, t.owners));
2954 
2955  if (t.offers)
2956  {
2957  auto const acctOffers = offersOnAccount(env, acct);
2958  if (acctOffers.size() > 0)
2959  {
2960  BEAST_EXPECT(acctOffers.size() == 1);
2961  auto const& acctOffer = *(acctOffers.front());
2962 
2963  BEAST_EXPECT(acctOffer[sfLedgerEntryType] == ltOFFER);
2964  BEAST_EXPECT(acctOffer[sfTakerGets] == t.takerGets);
2965  BEAST_EXPECT(acctOffer[sfTakerPays] == t.takerPays);
2966  }
2967  }
2968 
2969  // Give the next loop a clean slate by canceling any left-overs
2970  // in the offers.
2971  env(offer_cancel(acct, acctOfferSeq));
2972  env(offer_cancel(gw, gwOfferSeq));
2973  env.close();
2974  }
2975  }
2976 
2977  void
2979  {
2980  // Test a number of different corner cases regarding offer crossing
2981  // when both the tfSell flag and tfFillOrKill flags are set.
2982  testcase("Combine tfSell with tfFillOrKill");
2983 
2984  using namespace jtx;
2985 
2986  auto const gw = Account("gateway");
2987  auto const alice = Account("alice");
2988  auto const bob = Account("bob");
2989  auto const USD = gw["USD"];
2990 
2991  Env env{*this, features};
2992 
2993  env.fund(XRP(10000000), gw, alice, bob);
2994 
2995  // Code returned if an offer is killed.
2996  TER const killedCode{
2997  features[fix1578] ? TER{tecKILLED} : TER{tesSUCCESS}};
2998 
2999  // bob offers XRP for USD.
3000  env(trust(bob, USD(200)));
3001  env.close();
3002  env(pay(gw, bob, USD(100)));
3003  env.close();
3004  env(offer(bob, XRP(2000), USD(20)));
3005  env.close();
3006  {
3007  // alice submits a tfSell | tfFillOrKill offer that does not cross.
3008  env(offer(alice, USD(21), XRP(2100), tfSell | tfFillOrKill),
3009  ter(killedCode));
3010  env.close();
3011  env.require(balance(alice, USD(none)));
3012  env.require(offers(alice, 0));
3013  env.require(balance(bob, USD(100)));
3014  }
3015  {
3016  // alice submits a tfSell | tfFillOrKill offer that crosses.
3017  // Even though tfSell is present it doesn't matter this time.
3018  env(offer(alice, USD(20), XRP(2000), tfSell | tfFillOrKill));
3019  env.close();
3020  env.require(balance(alice, USD(20)));
3021  env.require(offers(alice, 0));
3022  env.require(balance(bob, USD(80)));
3023  }
3024  {
3025  // alice submits a tfSell | tfFillOrKill offer that crosses and
3026  // returns more than was asked for (because of the tfSell flag).
3027  env(offer(bob, XRP(2000), USD(20)));
3028  env.close();
3029  env(offer(alice, USD(10), XRP(1500), tfSell | tfFillOrKill));
3030  env.close();
3031  env.require(balance(alice, USD(35)));
3032  env.require(offers(alice, 0));
3033  env.require(balance(bob, USD(65)));
3034  }
3035  {
3036  // alice submits a tfSell | tfFillOrKill offer that doesn't cross.
3037  // This would have succeeded with a regular tfSell, but the
3038  // fillOrKill prevents the transaction from crossing since not
3039  // all of the offer is consumed.
3040 
3041  // We're using bob's left-over offer for XRP(500), USD(5)
3042  env(offer(alice, USD(1), XRP(501), tfSell | tfFillOrKill),
3043  ter(killedCode));
3044  env.close();
3045  env.require(balance(alice, USD(35)));
3046  env.require(offers(alice, 0));
3047  env.require(balance(bob, USD(65)));
3048  }
3049  {
3050  // Alice submits a tfSell | tfFillOrKill offer that finishes
3051  // off the remainder of bob's offer.
3052 
3053  // We're using bob's left-over offer for XRP(500), USD(5)
3054  env(offer(alice, USD(1), XRP(500), tfSell | tfFillOrKill));
3055  env.close();
3056  env.require(balance(alice, USD(40)));
3057  env.require(offers(alice, 0));
3058  env.require(balance(bob, USD(60)));
3059  }
3060  }
3061 
3062  void
3064  {
3065  testcase("Transfer Rate Offer");
3066 
3067  using namespace jtx;
3068 
3069  auto const gw1 = Account("gateway1");
3070  auto const USD = gw1["USD"];
3071 
3072  Env env{*this, features};
3073 
3074  // The fee that's charged for transactions.
3075  auto const fee = env.current()->fees().base;
3076 
3077  env.fund(XRP(100000), gw1);
3078  env.close();
3079 
3080  env(rate(gw1, 1.25));
3081  {
3082  auto const ann = Account("ann");
3083  auto const bob = Account("bob");
3084  env.fund(XRP(100) + reserve(env, 2) + (fee * 2), ann, bob);
3085  env.close();
3086 
3087  env(trust(ann, USD(200)));
3088  env(trust(bob, USD(200)));
3089  env.close();
3090 
3091  env(pay(gw1, bob, USD(125)));
3092  env.close();
3093 
3094  // bob offers to sell USD(100) for XRP. alice takes bob's offer.
3095  // Notice that although bob only offered USD(100), USD(125) was
3096  // removed from his account due to the gateway fee.
3097  //
3098  // A comparable payment would look like this:
3099  // env (pay (bob, alice, USD(100)), sendmax(USD(125)))
3100  env(offer(bob, XRP(1), USD(100)));
3101  env.close();
3102 
3103  env(offer(ann, USD(100), XRP(1)));
3104  env.close();
3105 
3106  env.require(balance(ann, USD(100)));
3107  env.require(balance(ann, XRP(99) + reserve(env, 2)));
3108  env.require(offers(ann, 0));
3109 
3110  env.require(balance(bob, USD(0)));
3111  env.require(balance(bob, XRP(101) + reserve(env, 2)));
3112  env.require(offers(bob, 0));
3113  }
3114  {
3115  // Reverse the order, so the offer in the books is to sell XRP
3116  // in return for USD. Gateway rate should still apply identically.
3117  auto const che = Account("che");
3118  auto const deb = Account("deb");
3119  env.fund(XRP(100) + reserve(env, 2) + (fee * 2), che, deb);
3120  env.close();
3121 
3122  env(trust(che, USD(200)));
3123  env(trust(deb, USD(200)));
3124  env.close();
3125 
3126  env(pay(gw1, deb, USD(125)));
3127  env.close();
3128 
3129  env(offer(che, USD(100), XRP(1)));
3130  env.close();
3131 
3132  env(offer(deb, XRP(1), USD(100)));
3133  env.close();
3134 
3135  env.require(balance(che, USD(100)));
3136  env.require(balance(che, XRP(99) + reserve(env, 2)));
3137  env.require(offers(che, 0));
3138 
3139  env.require(balance(deb, USD(0)));
3140  env.require(balance(deb, XRP(101) + reserve(env, 2)));
3141  env.require(offers(deb, 0));
3142  }
3143  {
3144  auto const eve = Account("eve");
3145  auto const fyn = Account("fyn");
3146 
3147  env.fund(XRP(20000) + (fee * 2), eve, fyn);
3148  env.close();
3149 
3150  env(trust(eve, USD(1000)));
3151  env(trust(fyn, USD(1000)));
3152  env.close();
3153 
3154  env(pay(gw1, eve, USD(100)));
3155  env(pay(gw1, fyn, USD(100)));
3156  env.close();
3157 
3158  // This test verifies that the amount removed from an offer
3159  // accounts for the transfer fee that is removed from the
3160  // account but not from the remaining offer.
3161  env(offer(eve, USD(10), XRP(4000)));
3162  env.close();
3163  std::uint32_t const eveOfferSeq = env.seq(eve) - 1;
3164 
3165  env(offer(fyn, XRP(2000), USD(5)));
3166  env.close();
3167 
3168  env.require(balance(eve, USD(105)));
3169  env.require(balance(eve, XRP(18000)));
3170  auto const evesOffers = offersOnAccount(env, eve);
3171  BEAST_EXPECT(evesOffers.size() == 1);
3172  if (evesOffers.size() != 0)
3173  {
3174  auto const& evesOffer = *(evesOffers.front());
3175  BEAST_EXPECT(evesOffer[sfLedgerEntryType] == ltOFFER);
3176  BEAST_EXPECT(evesOffer[sfTakerGets] == XRP(2000));
3177  BEAST_EXPECT(evesOffer[sfTakerPays] == USD(5));
3178  }
3179  env(offer_cancel(eve, eveOfferSeq)); // For later tests
3180 
3181  env.require(balance(fyn, USD(93.75)));
3182  env.require(balance(fyn, XRP(22000)));
3183  env.require(offers(fyn, 0));
3184  }
3185  // Start messing with two non-native currencies.
3186  auto const gw2 = Account("gateway2");
3187  auto const EUR = gw2["EUR"];
3188 
3189  env.fund(XRP(100000), gw2);
3190  env.close();
3191 
3192  env(rate(gw2, 1.5));
3193  {
3194  // Remove XRP from the equation. Give the two currencies two
3195  // different transfer rates so we can see both transfer rates
3196  // apply in the same transaction.
3197  auto const gay = Account("gay");
3198  auto const hal = Account("hal");
3199  env.fund(reserve(env, 3) + (fee * 3), gay, hal);
3200  env.close();
3201 
3202  env(trust(gay, USD(200)));
3203  env(trust(gay, EUR(200)));
3204  env(trust(hal, USD(200)));
3205  env(trust(hal, EUR(200)));
3206  env.close();
3207 
3208  env(pay(gw1, gay, USD(125)));
3209  env(pay(gw2, hal, EUR(150)));
3210  env.close();
3211 
3212  env(offer(gay, EUR(100), USD(100)));
3213  env.close();
3214 
3215  env(offer(hal, USD(100), EUR(100)));
3216  env.close();
3217 
3218  env.require(balance(gay, USD(0)));
3219  env.require(balance(gay, EUR(100)));
3220  env.require(balance(gay, reserve(env, 3)));
3221  env.require(offers(gay, 0));
3222 
3223  env.require(balance(hal, USD(100)));
3224  env.require(balance(hal, EUR(0)));
3225  env.require(balance(hal, reserve(env, 3)));
3226  env.require(offers(hal, 0));
3227  }
3228  {
3229  // A trust line's QualityIn should not affect offer crossing.
3230  auto const ivy = Account("ivy");
3231  auto const joe = Account("joe");
3232  env.fund(reserve(env, 3) + (fee * 3), ivy, joe);
3233  env.close();
3234 
3235  env(trust(ivy, USD(400)), qualityInPercent(90));
3236  env(trust(ivy, EUR(400)), qualityInPercent(80));
3237  env(trust(joe, USD(400)), qualityInPercent(70));
3238  env(trust(joe, EUR(400)), qualityInPercent(60));
3239  env.close();
3240 
3241  env(pay(gw1, ivy, USD(270)), sendmax(USD(500)));
3242  env(pay(gw2, joe, EUR(150)), sendmax(EUR(300)));
3243  env.close();
3244  env.require(balance(ivy, USD(300)));
3245  env.require(balance(joe, EUR(250)));
3246 
3247  env(offer(ivy, EUR(100), USD(200)));
3248  env.close();
3249 
3250  env(offer(joe, USD(200), EUR(100)));
3251  env.close();
3252 
3253  env.require(balance(ivy, USD(50)));
3254  env.require(balance(ivy, EUR(100)));
3255  env.require(balance(ivy, reserve(env, 3)));
3256  env.require(offers(ivy, 0));
3257 
3258  env.require(balance(joe, USD(200)));
3259  env.require(balance(joe, EUR(100)));
3260  env.require(balance(joe, reserve(env, 3)));
3261  env.require(offers(joe, 0));
3262  }
3263  {
3264  // A trust line's QualityOut should not affect offer crossing.
3265  auto const kim = Account("kim");
3266  auto const K_BUX = kim["BUX"];
3267  auto const lex = Account("lex");
3268  auto const meg = Account("meg");
3269  auto const ned = Account("ned");
3270  auto const N_BUX = ned["BUX"];
3271 
3272  // Verify trust line QualityOut affects payments.
3273  env.fund(reserve(env, 4) + (fee * 4), kim, lex, meg, ned);
3274  env.close();
3275 
3276  env(trust(lex, K_BUX(400)));
3277  env(trust(lex, N_BUX(200)), qualityOutPercent(120));
3278  env(trust(meg, N_BUX(100)));
3279  env.close();
3280  env(pay(ned, lex, N_BUX(100)));
3281  env.close();
3282  env.require(balance(lex, N_BUX(100)));
3283 
3284  env(pay(kim, meg, N_BUX(60)), path(lex, ned), sendmax(K_BUX(200)));
3285  env.close();
3286 
3287  env.require(balance(kim, K_BUX(none)));
3288  env.require(balance(kim, N_BUX(none)));
3289  env.require(balance(lex, K_BUX(72)));
3290  env.require(balance(lex, N_BUX(40)));
3291  env.require(balance(meg, K_BUX(none)));
3292  env.require(balance(meg, N_BUX(60)));
3293  env.require(balance(ned, K_BUX(none)));
3294  env.require(balance(ned, N_BUX(none)));
3295 
3296  // Now verify that offer crossing is unaffected by QualityOut.
3297  env(offer(lex, K_BUX(30), N_BUX(30)));
3298  env.close();
3299 
3300  env(offer(kim, N_BUX(30), K_BUX(30)));
3301  env.close();
3302 
3303  env.require(balance(kim, K_BUX(none)));
3304  env.require(balance(kim, N_BUX(30)));
3305  env.require(balance(lex, K_BUX(102)));
3306  env.require(balance(lex, N_BUX(10)));
3307  env.require(balance(meg, K_BUX(none)));
3308  env.require(balance(meg, N_BUX(60)));
3309  env.require(balance(ned, K_BUX(-30)));
3310  env.require(balance(ned, N_BUX(none)));
3311  }
3312  {
3313  // Make sure things work right when we're auto-bridging as well.
3314  auto const ova = Account("ova");
3315  auto const pat = Account("pat");
3316  auto const qae = Account("qae");
3317  env.fund(XRP(2) + reserve(env, 3) + (fee * 3), ova, pat, qae);
3318  env.close();
3319 
3320  // o ova has USD but wants XPR.
3321  // o pat has XRP but wants EUR.
3322  // o qae has EUR but wants USD.
3323  env(trust(ova, USD(200)));
3324  env(trust(ova, EUR(200)));
3325  env(trust(pat, USD(200)));
3326  env(trust(pat, EUR(200)));
3327  env(trust(qae, USD(200)));
3328  env(trust(qae, EUR(200)));
3329  env.close();
3330 
3331  env(pay(gw1, ova, USD(125)));
3332  env(pay(gw2, qae, EUR(150)));
3333  env.close();
3334 
3335  env(offer(ova, XRP(2), USD(100)));
3336  env(offer(pat, EUR(100), XRP(2)));
3337  env.close();
3338 
3339  env(offer(qae, USD(100), EUR(100)));
3340  env.close();
3341 
3342  env.require(balance(ova, USD(0)));
3343  env.require(balance(ova, EUR(0)));
3344  env.require(balance(ova, XRP(4) + reserve(env, 3)));
3345 
3346  // In pre-flow code ova's offer is left empty in the ledger.
3347  auto const ovasOffers = offersOnAccount(env, ova);
3348  if (ovasOffers.size() != 0)
3349  {
3350  BEAST_EXPECT(ovasOffers.size() == 1);
3351  auto const& ovasOffer = *(ovasOffers.front());
3352 
3353  BEAST_EXPECT(ovasOffer[sfLedgerEntryType] == ltOFFER);
3354  BEAST_EXPECT(ovasOffer[sfTakerGets] == USD(0));
3355  BEAST_EXPECT(ovasOffer[sfTakerPays] == XRP(0));
3356  }
3357 
3358  env.require(balance(pat, USD(0)));
3359  env.require(balance(pat, EUR(100)));
3360  env.require(balance(pat, XRP(0) + reserve(env, 3)));
3361  env.require(offers(pat, 0));
3362 
3363  env.require(balance(qae, USD(100)));
3364  env.require(balance(qae, EUR(0)));
3365  env.require(balance(qae, XRP(2) + reserve(env, 3)));
3366  env.require(offers(qae, 0));
3367  }
3368  }
3369 
3370  void
3372  {
3373  // The following test verifies some correct but slightly surprising
3374  // behavior in offer crossing. The scenario:
3375  //
3376  // o An entity has created one or more offers.
3377  // o The entity creates another offer that can be directly crossed
3378  // (not autobridged) by the previously created offer(s).
3379  // o Rather than self crossing the offers, delete the old offer(s).
3380  //
3381  // See a more complete explanation in the comments for
3382  // BookOfferCrossingStep::limitSelfCrossQuality().
3383  //
3384  // Note that, in this particular example, one offer causes several
3385  // crossable offers (worth considerably more than the new offer)
3386  // to be removed from the book.
3387  using namespace jtx;
3388 
3389  auto const gw = Account("gateway");
3390  auto const USD = gw["USD"];
3391 
3392  Env env{*this, features};
3393 
3394  // The fee that's charged for transactions.
3395  auto const fee = env.current()->fees().base;
3396  auto const startBalance = XRP(1000000);
3397 
3398  env.fund(startBalance + (fee * 4), gw);
3399  env.close();
3400 
3401  env(offer(gw, USD(60), XRP(600)));
3402  env.close();
3403  env(offer(gw, USD(60), XRP(600)));
3404  env.close();
3405  env(offer(gw, USD(60), XRP(600)));
3406  env.close();
3407 
3408  env.require(owners(gw, 3));
3409  env.require(balance(gw, startBalance + fee));
3410 
3411  auto gwOffers = offersOnAccount(env, gw);
3412  BEAST_EXPECT(gwOffers.size() == 3);
3413  for (auto const& offerPtr : gwOffers)
3414  {
3415  auto const& offer = *offerPtr;
3416  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3417  BEAST_EXPECT(offer[sfTakerGets] == XRP(600));
3418  BEAST_EXPECT(offer[sfTakerPays] == USD(60));
3419  }
3420 
3421  // Since this offer crosses the first offers, the previous offers
3422  // will be deleted and this offer will be put on the order book.
3423  env(offer(gw, XRP(1000), USD(100)));
3424  env.close();
3425  env.require(owners(gw, 1));
3426  env.require(offers(gw, 1));
3427  env.require(balance(gw, startBalance));
3428 
3429  gwOffers = offersOnAccount(env, gw);
3430  BEAST_EXPECT(gwOffers.size() == 1);
3431  for (auto const& offerPtr : gwOffers)
3432  {
3433  auto const& offer = *offerPtr;
3434  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3435  BEAST_EXPECT(offer[sfTakerGets] == USD(100));
3436  BEAST_EXPECT(offer[sfTakerPays] == XRP(1000));
3437  }
3438  }
3439 
3440  void
3442  {
3443  using namespace jtx;
3444 
3445  auto const gw1 = Account("gateway1");
3446  auto const gw2 = Account("gateway2");
3447  auto const alice = Account("alice");
3448  auto const USD = gw1["USD"];
3449  auto const EUR = gw2["EUR"];
3450 
3451  Env env{*this, features};
3452 
3453  env.fund(XRP(1000000), gw1, gw2);
3454  env.close();
3455 
3456  // The fee that's charged for transactions.
3457  auto const f = env.current()->fees().base;
3458 
3459  // Test cases
3460  struct TestData
3461  {
3462  std::string acct; // Account operated on
3463  STAmount fundXRP; // XRP acct funded with
3464  STAmount fundUSD; // USD acct funded with
3465  STAmount fundEUR; // EUR acct funded with
3466  TER firstOfferTec; // tec code on first offer
3467  TER secondOfferTec; // tec code on second offer
3468  };
3469 
3470  // clang-format off
3471  TestData const tests[]{
3472  // acct fundXRP fundUSD fundEUR firstOfferTec secondOfferTec
3473  {"ann", reserve(env, 3) + f * 4, USD(1000), EUR(1000), tesSUCCESS, tesSUCCESS},
3474  {"bev", reserve(env, 3) + f * 4, USD( 1), EUR(1000), tesSUCCESS, tesSUCCESS},
3475  {"cam", reserve(env, 3) + f * 4, USD(1000), EUR( 1), tesSUCCESS, tesSUCCESS},
3476  {"deb", reserve(env, 3) + f * 4, USD( 0), EUR( 1), tesSUCCESS, tecUNFUNDED_OFFER},
3477  {"eve", reserve(env, 3) + f * 4, USD( 1), EUR( 0), tecUNFUNDED_OFFER, tesSUCCESS},
3478  {"flo", reserve(env, 3) + 0, USD(1000), EUR(1000), tecINSUF_RESERVE_OFFER, tecINSUF_RESERVE_OFFER},
3479  };
3480  //clang-format on
3481 
3482  for (auto const& t : tests)
3483  {
3484  auto const acct = Account{t.acct};
3485  env.fund(t.fundXRP, acct);
3486  env.close();
3487 
3488  env(trust(acct, USD(1000)));
3489  env(trust(acct, EUR(1000)));
3490  env.close();
3491 
3492  if (t.fundUSD > USD(0))
3493  env(pay(gw1, acct, t.fundUSD));
3494  if (t.fundEUR > EUR(0))
3495  env(pay(gw2, acct, t.fundEUR));
3496  env.close();
3497 
3498  env(offer(acct, USD(500), EUR(600)), ter(t.firstOfferTec));
3499  env.close();
3500  std::uint32_t const firstOfferSeq = env.seq(acct) - 1;
3501 
3502  int offerCount = t.firstOfferTec == tesSUCCESS ? 1 : 0;
3503  env.require(owners(acct, 2 + offerCount));
3504  env.require(balance(acct, t.fundUSD));
3505  env.require(balance(acct, t.fundEUR));
3506 
3507  auto acctOffers = offersOnAccount(env, acct);
3508  BEAST_EXPECT(acctOffers.size() == offerCount);
3509  for (auto const& offerPtr : acctOffers)
3510  {
3511  auto const& offer = *offerPtr;
3512  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3513  BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
3514  BEAST_EXPECT(offer[sfTakerPays] == USD(500));
3515  }
3516 
3517  env(offer(acct, EUR(600), USD(500)), ter(t.secondOfferTec));
3518  env.close();
3519  std::uint32_t const secondOfferSeq = env.seq(acct) - 1;
3520 
3521  offerCount = t.secondOfferTec == tesSUCCESS ? 1 : offerCount;
3522  env.require(owners(acct, 2 + offerCount));
3523  env.require(balance(acct, t.fundUSD));
3524  env.require(balance(acct, t.fundEUR));
3525 
3526  acctOffers = offersOnAccount(env, acct);
3527  BEAST_EXPECT(acctOffers.size() == offerCount);
3528  for (auto const& offerPtr : acctOffers)
3529  {
3530  auto const& offer = *offerPtr;
3531  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3532  if (offer[sfSequence] == firstOfferSeq)
3533  {
3534  BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
3535  BEAST_EXPECT(offer[sfTakerPays] == USD(500));
3536  }
3537  else
3538  {
3539  BEAST_EXPECT(offer[sfTakerGets] == USD(500));
3540  BEAST_EXPECT(offer[sfTakerPays] == EUR(600));
3541  }
3542  }
3543 
3544  // Remove any offers from acct for the next pass.
3545  env(offer_cancel(acct, firstOfferSeq));
3546  env.close();
3547  env(offer_cancel(acct, secondOfferSeq));
3548  env.close();
3549  }
3550  }
3551 
3552  void
3554  {
3555  testcase("Self Cross Offer");
3556  testSelfCrossOffer1(features);
3557  testSelfCrossOffer2(features);
3558  }
3559 
3560  void
3562  {
3563  // Folks who issue their own currency have, in effect, as many
3564  // funds as they are trusted for. This test used to fail because
3565  // self-issuing was not properly checked. Verify that it works
3566  // correctly now.
3567  using namespace jtx;
3568 
3569  Env env{*this, features};
3570 
3571  auto const alice = Account("alice");
3572  auto const bob = Account("bob");
3573  auto const USD = bob["USD"];
3574  auto const f = env.current()->fees().base;
3575 
3576  env.fund(XRP(50000) + f, alice, bob);
3577  env.close();
3578 
3579  env(offer(alice, USD(5000), XRP(50000)));
3580  env.close();
3581 
3582  // This offer should take alice's offer up to Alice's reserve.
3583  env(offer(bob, XRP(50000), USD(5000)));
3584  env.close();
3585 
3586  // alice's offer should have been removed, since she's down to her
3587  // XRP reserve.
3588  env.require(balance(alice, XRP(250)));
3589  env.require(owners(alice, 1));
3590  env.require(lines(alice, 1));
3591 
3592  // However bob's offer should be in the ledger, since it was not
3593  // fully crossed.
3594  auto const bobOffers = offersOnAccount(env, bob);
3595  BEAST_EXPECT(bobOffers.size() == 1);
3596  for (auto const& offerPtr : bobOffers)
3597  {
3598  auto const& offer = *offerPtr;
3599  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3600  BEAST_EXPECT(offer[sfTakerGets] == USD(25));
3601  BEAST_EXPECT(offer[sfTakerPays] == XRP(250));
3602  }
3603  }
3604 
3605  void
3607  {
3608  // At one point in the past this invalid path caused an assert. It
3609  // should not be possible for user-supplied data to cause an assert.
3610  // Make sure the assert is gone.
3611  testcase("Bad path assert");
3612 
3613  using namespace jtx;
3614 
3615  // The problem was identified when featureOwnerPaysFee was enabled,
3616  // so make sure that gets included.
3617  Env env{*this, features | featureOwnerPaysFee};
3618 
3619  // The fee that's charged for transactions.
3620  auto const fee = env.current()->fees().base;
3621  {
3622  // A trust line's QualityOut should not affect offer crossing.
3623  auto const ann = Account("ann");
3624  auto const A_BUX = ann["BUX"];
3625  auto const bob = Account("bob");
3626  auto const cam = Account("cam");
3627  auto const dan = Account("dan");
3628  auto const D_BUX = dan["BUX"];
3629 
3630  // Verify trust line QualityOut affects payments.
3631  env.fund(reserve(env, 4) + (fee * 4), ann, bob, cam, dan);
3632  env.close();
3633 
3634  env(trust(bob, A_BUX(400)));
3635  env(trust(bob, D_BUX(200)), qualityOutPercent(120));
3636  env(trust(cam, D_BUX(100)));
3637  env.close();
3638  env(pay(dan, bob, D_BUX(100)));
3639  env.close();
3640  env.require(balance(bob, D_BUX(100)));
3641 
3642  env(pay(ann, cam, D_BUX(60)), path(bob, dan), sendmax(A_BUX(200)));
3643  env.close();
3644 
3645  env.require(balance(ann, A_BUX(none)));
3646  env.require(balance(ann, D_BUX(none)));
3647  env.require(balance(bob, A_BUX(72)));
3648  env.require(balance(bob, D_BUX(40)));
3649  env.require(balance(cam, A_BUX(none)));
3650  env.require(balance(cam, D_BUX(60)));
3651  env.require(balance(dan, A_BUX(none)));
3652  env.require(balance(dan, D_BUX(none)));
3653 
3654  env(offer(bob, A_BUX(30), D_BUX(30)));
3655  env.close();
3656 
3657  env(trust(ann, D_BUX(100)));
3658  env.close();
3659 
3660  // This payment caused the assert.
3661  env(pay(ann, ann, D_BUX(30)),
3662  path(A_BUX, D_BUX),
3663  sendmax(A_BUX(30)),
3664  ter(temBAD_PATH));
3665  env.close();
3666 
3667  env.require(balance(ann, A_BUX(none)));
3668  env.require(balance(ann, D_BUX(0)));
3669  env.require(balance(bob, A_BUX(72)));
3670  env.require(balance(bob, D_BUX(40)));
3671  env.require(balance(cam, A_BUX(none)));
3672  env.require(balance(cam, D_BUX(60)));
3673  env.require(balance(dan, A_BUX(0)));
3674  env.require(balance(dan, D_BUX(none)));
3675  }
3676  }
3677 
3678  void
3680  {
3681  // The offer crossing code expects that a DirectStep is always
3682  // preceded by a BookStep. In one instance the default path
3683  // was not matching that assumption. Here we recreate that case
3684  // so we can prove the bug stays fixed.
3685  testcase("Direct to Direct path");
3686 
3687  using namespace jtx;
3688 
3689  Env env{*this, features};
3690 
3691  auto const ann = Account("ann");
3692  auto const bob = Account("bob");
3693  auto const cam = Account("cam");
3694  auto const A_BUX = ann["BUX"];
3695  auto const B_BUX = bob["BUX"];
3696 
3697  auto const fee = env.current()->fees().base;
3698  env.fund(reserve(env, 4) + (fee * 5), ann, bob, cam);
3699  env.close();
3700 
3701  env(trust(ann, B_BUX(40)));
3702  env(trust(cam, A_BUX(40)));
3703  env(trust(cam, B_BUX(40)));
3704  env.close();
3705 
3706  env(pay(ann, cam, A_BUX(35)));
3707  env(pay(bob, cam, B_BUX(35)));
3708 
3709  env(offer(bob, A_BUX(30), B_BUX(30)));
3710  env.close();
3711 
3712  // cam puts an offer on the books that her upcoming offer could cross.
3713  // But this offer should be deleted, not crossed, by her upcoming
3714  // offer.
3715  env(offer(cam, A_BUX(29), B_BUX(30), tfPassive));
3716  env.close();
3717  env.require(balance(cam, A_BUX(35)));
3718  env.require(balance(cam, B_BUX(35)));
3719  env.require(offers(cam, 1));
3720 
3721  // This offer caused the assert.
3722  env(offer(cam, B_BUX(30), A_BUX(30)));
3723  env.close();
3724 
3725  env.require(balance(bob, A_BUX(30)));
3726  env.require(balance(cam, A_BUX(5)));
3727  env.require(balance(cam, B_BUX(65)));
3728  env.require(offers(cam, 0));
3729  }
3730 
3731  void
3733  {
3734  // The Flow offer crossing code used to assert if an offer was made
3735  // for more XRP than the offering account held. This unit test
3736  // reproduces that failing case.
3737  testcase("Self crossing low quality offer");
3738 
3739  using namespace jtx;
3740 
3741  Env env{*this, features};
3742 
3743  auto const ann = Account("ann");
3744  auto const gw = Account("gateway");
3745  auto const BTC = gw["BTC"];
3746 
3747  auto const fee = env.current()->fees().base;
3748  env.fund(reserve(env, 2) + drops(9999640) + (fee), ann);
3749  env.fund(reserve(env, 2) + (fee * 4), gw);
3750  env.close();
3751 
3752  env(rate(gw, 1.002));
3753  env(trust(ann, BTC(10)));
3754  env.close();
3755 
3756  env(pay(gw, ann, BTC(2.856)));
3757  env.close();
3758 
3759  env(offer(ann, drops(365611702030), BTC(5.713)));
3760  env.close();
3761 
3762  // This offer caused the assert.
3763  env(offer(ann, BTC(0.687), drops(20000000000)),
3765  }
3766 
3767  void
3769  {
3770  // The Flow offer crossing code had a case where it was not rounding
3771  // the offer crossing correctly after a partial crossing. The
3772  // failing case was found on the network. Here we add the case to
3773  // the unit tests.
3774  testcase("Offer In Scaling");
3775 
3776  using namespace jtx;
3777 
3778  Env env{*this, features};
3779 
3780  auto const gw = Account("gateway");
3781  auto const alice = Account("alice");
3782  auto const bob = Account("bob");
3783  auto const CNY = gw["CNY"];
3784 
3785  auto const fee = env.current()->fees().base;
3786  env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
3787  env.fund(reserve(env, 2) + (fee * 4), gw);
3788  env.close();
3789 
3790  env(trust(bob, CNY(500)));
3791  env.close();
3792 
3793  env(pay(gw, bob, CNY(300)));
3794  env.close();
3795 
3796  env(offer(bob, drops(5400000000), CNY(216.054)));
3797  env.close();
3798 
3799  // This offer did not round result of partial crossing correctly.
3800  env(offer(alice, CNY(13562.0001), drops(339000000000)));
3801  env.close();
3802 
3803  auto const aliceOffers = offersOnAccount(env, alice);
3804  BEAST_EXPECT(aliceOffers.size() == 1);
3805  for (auto const& offerPtr : aliceOffers)
3806  {
3807  auto const& offer = *offerPtr;
3808  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3809  BEAST_EXPECT(offer[sfTakerGets] == drops(333599446582));
3810  BEAST_EXPECT(offer[sfTakerPays] == CNY(13345.9461));
3811  }
3812  }
3813 
3814  void
3816  {
3817  // After adding the previous case, there were still failing rounding
3818  // cases in Flow offer crossing. This one was because the gateway
3819  // transfer rate was not being correctly handled.
3820  testcase("Offer In Scaling With Xfer Rate");
3821 
3822  using namespace jtx;
3823 
3824  Env env{*this, features};
3825 
3826  auto const gw = Account("gateway");
3827  auto const alice = Account("alice");
3828  auto const bob = Account("bob");
3829  auto const BTC = gw["BTC"];
3830  auto const JPY = gw["JPY"];
3831 
3832  auto const fee = env.current()->fees().base;
3833  env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
3834  env.fund(reserve(env, 2) + (fee * 4), gw);
3835  env.close();
3836 
3837  env(rate(gw, 1.002));
3838  env(trust(alice, JPY(4000)));
3839  env(trust(bob, BTC(2)));
3840  env.close();
3841 
3842  env(pay(gw, alice, JPY(3699.034802280317)));
3843  env(pay(gw, bob, BTC(1.156722559140311)));
3844  env.close();
3845 
3846  env(offer(bob, JPY(1241.913390770747), BTC(0.01969825690469254)));
3847  env.close();
3848 
3849  // This offer did not round result of partial crossing correctly.
3850  env(offer(alice, BTC(0.05507568706427876), JPY(3472.696773391072)));
3851  env.close();
3852 
3853  auto const aliceOffers = offersOnAccount(env, alice);
3854  BEAST_EXPECT(aliceOffers.size() == 1);
3855  for (auto const& offerPtr : aliceOffers)
3856  {
3857  auto const& offer = *offerPtr;
3858  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3859  BEAST_EXPECT(
3860  offer[sfTakerGets] ==
3861  STAmount(JPY.issue(), std::uint64_t(2230682446713524ul), -12));
3862  BEAST_EXPECT(offer[sfTakerPays] == BTC(0.035378));
3863  }
3864  }
3865 
3866  void
3868  {
3869  // Another instance where Flow offer crossing was not always
3870  // working right was if the Taker had fewer funds than the Offer
3871  // was offering. The basis for this test came off the network.
3872  testcase("Offer Threshold With Reduced Funds");
3873 
3874  using namespace jtx;
3875 
3876  Env env{*this, features};
3877 
3878  auto const gw1 = Account("gw1");
3879  auto const gw2 = Account("gw2");
3880  auto const alice = Account("alice");
3881  auto const bob = Account("bob");
3882  auto const USD = gw1["USD"];
3883  auto const JPY = gw2["JPY"];
3884 
3885  auto const fee = env.current()->fees().base;
3886  env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
3887  env.fund(reserve(env, 2) + (fee * 4), gw1, gw2);
3888  env.close();
3889 
3890  env(rate(gw1, 1.002));
3891  env(trust(alice, USD(1000)));
3892  env(trust(bob, JPY(100000)));
3893  env.close();
3894 
3895  env(
3896  pay(gw1,
3897  alice,
3898  STAmount{USD.issue(), std::uint64_t(2185410179555600), -14}));
3899  env(
3900  pay(gw2,
3901  bob,
3902  STAmount{JPY.issue(), std::uint64_t(6351823459548956), -12}));
3903  env.close();
3904 
3905  env(offer(
3906  bob,
3907  STAmount{USD.issue(), std::uint64_t(4371257532306000), -17},
3908  STAmount{JPY.issue(), std::uint64_t(4573216636606000), -15}));
3909  env.close();
3910 
3911  // This offer did not partially cross correctly.
3912  env(offer(
3913  alice,
3914  STAmount{JPY.issue(), std::uint64_t(2291181510070762), -12},
3915  STAmount{USD.issue(), std::uint64_t(2190218999914694), -14}));
3916  env.close();
3917 
3918  auto const aliceOffers = offersOnAccount(env, alice);
3919  BEAST_EXPECT(aliceOffers.size() == 1);
3920  for (auto const& offerPtr : aliceOffers)
3921  {
3922  auto const& offer = *offerPtr;
3923  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3924  BEAST_EXPECT(
3925  offer[sfTakerGets] ==
3926  STAmount(USD.issue(), std::uint64_t(2185847305256635), -14));
3927  BEAST_EXPECT(
3928  offer[sfTakerPays] ==
3929  STAmount(JPY.issue(), std::uint64_t(2286608293434156), -12));
3930  }
3931  }
3932 
3933  void
3935  {
3936  testcase("Tiny Offer");
3937 
3938  using namespace jtx;
3939 
3940  Env env{*this, features};
3941 
3942  auto const gw = Account("gw");
3943  auto const alice = Account("alice");
3944  auto const bob = Account("bob");
3945  auto const CNY = gw["CNY"];
3946  auto const fee = env.current()->fees().base;
3947  auto const startXrpBalance = drops(400000000000) + (fee * 2);
3948 
3949  env.fund(startXrpBalance, gw, alice, bob);
3950  env.close();
3951 
3952  env(trust(bob, CNY(100000)));
3953  env.close();
3954 
3955  // Place alice's tiny offer in the book first. Let's see what happens
3956  // when a reasonable offer crosses it.
3957  STAmount const alicesCnyOffer{
3958  CNY.issue(), std::uint64_t(4926000000000000), -23};
3959 
3960  env(offer(alice, alicesCnyOffer, drops(1), tfPassive));
3961  env.close();
3962 
3963  // bob places an ordinary offer
3964  STAmount const bobsCnyStartBalance{
3965  CNY.issue(), std::uint64_t(3767479960090235), -15};
3966  env(pay(gw, bob, bobsCnyStartBalance));
3967  env.close();
3968 
3969  env(offer(
3970  bob,
3971  drops(203),
3972  STAmount{CNY.issue(), std::uint64_t(1000000000000000), -20}));
3973  env.close();
3974 
3975  env.require(balance(alice, alicesCnyOffer));
3976  env.require(balance(alice, startXrpBalance - fee - drops(1)));
3977  env.require(balance(bob, bobsCnyStartBalance - alicesCnyOffer));
3978  env.require(balance(bob, startXrpBalance - (fee * 2) + drops(1)));
3979  }
3980 
3981  void
3983  {
3984  testcase("Self Pay Xfer Fee");
3985  // The old offer crossing code does not charge a transfer fee
3986  // if alice pays alice. That's different from how payments work.
3987  // Payments always charge a transfer fee even if the money is staying
3988  // in the same hands.
3989  //
3990  // What's an example where alice pays alice? There are three actors:
3991  // gw, alice, and bob.
3992  //
3993  // 1. gw issues BTC and USD. qw charges a 0.2% transfer fee.
3994  //
3995  // 2. alice makes an offer to buy XRP and sell USD.
3996  // 3. bob makes an offer to buy BTC and sell XRP.
3997  //
3998  // 4. alice now makes an offer to sell BTC and buy USD.
3999  //
4000  // This last offer crosses using auto-bridging.
4001  // o alice's last offer sells BTC to...
4002  // o bob' offer which takes alice's BTC and sells XRP to...
4003  // o alice's first offer which takes bob's XRP and sells USD to...
4004  // o alice's last offer.
4005  //
4006  // So alice sells USD to herself.
4007  //
4008  // There are six cases that we need to test:
4009  // o alice crosses her own offer on the first leg (BTC).
4010  // o alice crosses her own offer on the second leg (USD).
4011  // o alice crosses her own offers on both legs.
4012  // All three cases need to be tested:
4013  // o In reverse (alice has enough BTC to cover her offer) and
4014  // o Forward (alice owns less BTC than is in her final offer.
4015  //
4016  // It turns out that two of the forward cases fail for a different
4017  // reason. They are therefore commented out here, But they are
4018  // revisited in the testSelfPayUnlimitedFunds() unit test.
4019 
4020  using namespace jtx;
4021 
4022  Env env{*this, features};
4023 
4024  auto const gw = Account("gw");
4025  auto const BTC = gw["BTC"];
4026  auto const USD = gw["USD"];
4027  auto const startXrpBalance = XRP(4000000);
4028 
4029  env.fund(startXrpBalance, gw);
4030  env.close();
4031 
4032  env(rate(gw, 1.25));
4033  env.close();
4034 
4035  // Test cases
4036  struct Actor
4037  {
4038  Account acct;
4039  int offers; // offers on account after crossing
4040  PrettyAmount xrp; // final expected after crossing
4041  PrettyAmount btc; // final expected after crossing
4042  PrettyAmount usd; // final expected after crossing
4043  };
4044  struct TestData
4045  {
4046  // The first three three integers give the *index* in actors
4047  // to assign each of the three roles. By using indices it is
4048  // easy for alice to own the offer in the first leg, the second
4049  // leg, or both.
4050  std::size_t self;
4051  std::size_t leg0;
4052  std::size_t leg1;
4053  PrettyAmount btcStart;
4054  std::vector<Actor> actors;
4055  };
4056 
4057  // clang-format off
4058  TestData const tests[]{
4059  // btcStart --------------------- actor[0] --------------------- -------------------- actor[1] -------------------
4060  {0, 0, 1, BTC(20), {{"ann", 0, drops(3899999999960), BTC(20.0), USD(3000)}, {"abe", 0, drops(4099999999970), BTC( 0), USD(750)}}}, // no BTC xfer fee
4061  {0, 1, 0, BTC(20), {{"bev", 0, drops(4099999999960), BTC( 7.5), USD(2000)}, {"bob", 0, drops(3899999999970), BTC(10), USD( 0)}}}, // no USD xfer fee
4062  {0, 0, 0, BTC(20), {{"cam", 0, drops(3999999999950), BTC(20.0), USD(2000)} }}, // no xfer fee
4063  {0, 1, 0, BTC( 5), {{"deb", 1, drops(4039999999960), BTC( 0.0), USD(2000)}, {"dan", 1, drops(3959999999970), BTC( 4), USD( 0)}}}, // no USD xfer fee
4064  };
4065  // clang-format on
4066 
4067  for (auto const& t : tests)
4068  {
4069  Account const& self = t.actors[t.self].acct;
4070  Account const& leg0 = t.actors[t.leg0].acct;
4071  Account const& leg1 = t.actors[t.leg1].acct;
4072 
4073  for (auto const& actor : t.actors)
4074  {
4075  env.fund(XRP(4000000), actor.acct);
4076  env.close();
4077 
4078  env(trust(actor.acct, BTC(40)));
4079  env(trust(actor.acct, USD(8000)));
4080  env.close();
4081  }
4082 
4083  env(pay(gw, self, t.btcStart));
4084  env(pay(gw, self, USD(2000)));
4085  if (self.id() != leg1.id())
4086  env(pay(gw, leg1, USD(2000)));
4087  env.close();
4088 
4089  // Get the initial offers in place. Remember their sequences
4090  // so we can delete them later.
4091  env(offer(leg0, BTC(10), XRP(100000), tfPassive));
4092  env.close();
4093  std::uint32_t const leg0OfferSeq = env.seq(leg0) - 1;
4094 
4095  env(offer(leg1, XRP(100000), USD(1000), tfPassive));
4096  env.close();
4097  std::uint32_t const leg1OfferSeq = env.seq(leg1) - 1;
4098 
4099  // This is the offer that matters.
4100  env(offer(self, USD(1000), BTC(10)));
4101  env.close();
4102  std::uint32_t const selfOfferSeq = env.seq(self) - 1;
4103 
4104  // Verify results.
4105  for (auto const& actor : t.actors)
4106  {
4107  // Sometimes Taker crossing gets lazy about deleting offers.
4108  // Treat an empty offer as though it is deleted.
4109  auto actorOffers = offersOnAccount(env, actor.acct);
4110  auto const offerCount = std::distance(
4111  actorOffers.begin(),
4113  actorOffers.begin(),
4114  actorOffers.end(),
4115  [](std::shared_ptr<SLE const>& offer) {
4116  return (*offer)[sfTakerGets].signum() == 0;
4117  }));
4118  BEAST_EXPECT(offerCount == actor.offers);
4119 
4120  env.require(balance(actor.acct, actor.xrp));
4121  env.require(balance(actor.acct, actor.btc));
4122  env.require(balance(actor.acct, actor.usd));
4123  }
4124  // Remove any offers that might be left hanging around. They
4125  // could bollix up later loops.
4126  env(offer_cancel(leg0, leg0OfferSeq));
4127  env.close();
4128  env(offer_cancel(leg1, leg1OfferSeq));
4129  env.close();
4130  env(offer_cancel(self, selfOfferSeq));
4131  env.close();
4132  }
4133  }
4134 
4135  void
4137  {
4138  testcase("Self Pay Unlimited Funds");
4139  // The Taker offer crossing code recognized when Alice was paying
4140  // Alice the same denomination. In this case, as long as Alice
4141  // has a little bit of that denomination, it treats Alice as though
4142  // she has unlimited funds in that denomination.
4143  //
4144  // Huh? What kind of sense does that make?
4145  //
4146  // One way to think about it is to break a single payment into a
4147  // series of very small payments executed sequentially but very
4148  // quickly. Alice needs to pay herself 1 USD, but she only has
4149  // 0.01 USD. Alice says, "Hey Alice, let me pay you a penny."
4150  // Alice does this, taking the penny out of her pocket and then
4151  // putting it back in her pocket. Then she says, "Hey Alice,
4152  // I found another penny. I can pay you another penny." Repeat
4153  // these steps 100 times and Alice has paid herself 1 USD even though
4154  // she only owns 0.01 USD.
4155  //
4156  // That's all very nice, but the payment code does not support this
4157  // optimization. In part that's because the payment code can
4158  // operate on a whole batch of offers. As a matter of fact, it can
4159  // deal in two consecutive batches of offers. It would take a great
4160  // deal of sorting out to figure out which offers in the two batches
4161  // had the same owner and give them special processing. And,
4162  // honestly, it's a weird little corner case.
4163  //
4164  // So, since Flow offer crossing uses the payments engine, Flow
4165  // offer crossing no longer supports this optimization.
4166  //
4167  // The following test shows the difference in the behaviors between
4168  // Taker offer crossing and Flow offer crossing.
4169 
4170  using namespace jtx;
4171 
4172  Env env{*this, features};
4173 
4174  auto const gw = Account("gw");
4175  auto const BTC = gw["BTC"];
4176  auto const USD = gw["USD"];
4177  auto const startXrpBalance = XRP(4000000);
4178 
4179  env.fund(startXrpBalance, gw);
4180  env.close();
4181 
4182  env(rate(gw, 1.25));
4183  env.close();
4184 
4185  // Test cases
4186  struct Actor
4187  {
4188  Account acct;
4189  int offers; // offers on account after crossing
4190  PrettyAmount xrp; // final expected after crossing
4191  PrettyAmount btc; // final expected after crossing
4192  PrettyAmount usd; // final expected after crossing
4193  };
4194  struct TestData
4195  {
4196  // The first three three integers give the *index* in actors
4197  // to assign each of the three roles. By using indices it is
4198  // easy for alice to own the offer in the first leg, the second
4199  // leg, or both.
4200  std::size_t self;
4201  std::size_t leg0;
4202  std::size_t leg1;
4203  PrettyAmount btcStart;
4204  std::vector<Actor> actors;
4205  };
4206 
4207  // clang-format off
4208  TestData const takerTests[]{
4209  // btcStart ------------------- actor[0] -------------------- ------------------- actor[1] --------------------
4210  {0, 0, 1, BTC(5), {{"deb", 0, drops(3899999999960), BTC(5), USD(3000)}, {"dan", 0, drops(4099999999970), BTC(0), USD(750)}}}, // no BTC xfer fee
4211  {0, 0, 0, BTC(5), {{"flo", 0, drops(3999999999950), BTC(5), USD(2000)} }} // no xfer fee
4212  };
4213 
4214  TestData const flowTests[]{
4215  // btcStart ------------------- actor[0] -------------------- ------------------- actor[1] --------------------
4216  {0, 0, 1, BTC(5), {{"gay", 1, drops(3949999999960), BTC(5), USD(2500)}, {"gar", 1, drops(4049999999970), BTC(0), USD(1375)}}}, // no BTC xfer fee
4217  {0, 0, 0, BTC(5), {{"hye", 2, drops(3999999999950), BTC(5), USD(2000)} }} // no xfer fee
4218  };
4219  // clang-format on
4220 
4221  // Pick the right tests.
4222  auto const& tests = features[featureFlowCross] ? flowTests : takerTests;
4223 
4224  for (auto const& t : tests)
4225  {
4226  Account const& self = t.actors[t.self].acct;
4227  Account const& leg0 = t.actors[t.leg0].acct;
4228  Account const& leg1 = t.actors[t.leg1].acct;
4229 
4230  for (auto const& actor : t.actors)
4231  {
4232  env.fund(XRP(4000000), actor.acct);
4233  env.close();
4234 
4235  env(trust(actor.acct, BTC(40)));
4236  env(trust(actor.acct, USD(8000)));
4237  env.close();
4238  }
4239 
4240  env(pay(gw, self, t.btcStart));
4241  env(pay(gw, self, USD(2000)));
4242  if (self.id() != leg1.id())
4243  env(pay(gw, leg1, USD(2000)));
4244  env.close();
4245 
4246  // Get the initial offers in place. Remember their sequences
4247  // so we can delete them later.
4248  env(offer(leg0, BTC(10), XRP(100000), tfPassive));
4249  env.close();
4250  std::uint32_t const leg0OfferSeq = env.seq(leg0) - 1;
4251 
4252  env(offer(leg1, XRP(100000), USD(1000), tfPassive));
4253  env.close();
4254  std::uint32_t const leg1OfferSeq = env.seq(leg1) - 1;
4255 
4256  // This is the offer that matters.
4257  env(offer(self, USD(1000), BTC(10)));
4258  env.close();
4259  std::uint32_t const selfOfferSeq = env.seq(self) - 1;
4260 
4261  // Verify results.
4262  for (auto const& actor : t.actors)
4263  {
4264  // Sometimes Taker offer crossing gets lazy about deleting
4265  // offers. Treat an empty offer as though it is deleted.
4266  auto actorOffers = offersOnAccount(env, actor.acct);
4267  auto const offerCount = std::distance(
4268  actorOffers.begin(),
4270  actorOffers.begin(),
4271  actorOffers.end(),
4272  [](std::shared_ptr<SLE const>& offer) {
4273  return (*offer)[sfTakerGets].signum() == 0;
4274  }));
4275  BEAST_EXPECT(offerCount == actor.offers);
4276 
4277  env.require(balance(actor.acct, actor.xrp));
4278  env.require(balance(actor.acct, actor.btc));
4279  env.require(balance(actor.acct, actor.usd));
4280  }
4281  // Remove any offers that might be left hanging around. They
4282  // could bollix up later loops.
4283  env(offer_cancel(leg0, leg0OfferSeq));
4284  env.close();
4285  env(offer_cancel(leg1, leg1OfferSeq));
4286  env.close();
4287  env(offer_cancel(self, selfOfferSeq));
4288  env.close();
4289  }
4290  }
4291 
4292  void
4294  {
4295  testcase("lsfRequireAuth");
4296 
4297  using namespace jtx;
4298 
4299  Env env{*this, features};
4300 
4301  auto const gw = Account("gw");
4302  auto const alice = Account("alice");
4303  auto const bob = Account("bob");
4304  auto const gwUSD = gw["USD"];
4305  auto const aliceUSD = alice["USD"];
4306  auto const bobUSD = bob["USD"];
4307 
4308  env.fund(XRP(400000), gw, alice, bob);
4309  env.close();
4310 
4311  // GW requires authorization for holders of its IOUs
4312  env(fset(gw, asfRequireAuth));
4313  env.close();
4314 
4315  // Properly set trust and have gw authorize bob and alice
4316  env(trust(gw, bobUSD(100)), txflags(tfSetfAuth));
4317  env(trust(bob, gwUSD(100)));
4318  env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
4319  env(trust(alice, gwUSD(100)));
4320  // Alice is able to place the offer since the GW has authorized her
4321  env(offer(alice, gwUSD(40), XRP(4000)));
4322  env.close();
4323 
4324  env.require(offers(alice, 1));
4325  env.require(balance(alice, gwUSD(0)));
4326 
4327  env(pay(gw, bob, gwUSD(50)));
4328  env.close();
4329 
4330  env.require(balance(bob, gwUSD(50)));
4331 
4332  // Bob's offer should cross Alice's
4333  env(offer(bob, XRP(4000), gwUSD(40)));
4334  env.close();
4335 
4336  env.require(offers(alice, 0));
4337  env.require(balance(alice, gwUSD(40)));
4338 
4339  env.require(offers(bob, 0));
4340  env.require(balance(bob, gwUSD(10)));
4341  }
4342 
4343  void
4345  {
4346  testcase("Missing Auth");
4347  // 1. alice creates an offer to acquire USD/gw, an asset for which
4348  // she does not have a trust line. At some point in the future,
4349  // gw adds lsfRequireAuth. Then, later, alice's offer is crossed.
4350  // a. With Taker alice's unauthorized offer is consumed.
4351  // b. With FlowCross alice's offer is deleted, not consumed,
4352  // since alice is not authorized to hold USD/gw.
4353  //
4354  // 2. alice tries to create an offer for USD/gw, now that gw has
4355  // lsfRequireAuth set. This time the offer create fails because
4356  // alice is not authorized to hold USD/gw.
4357  //
4358  // 3. Next, gw creates a trust line to alice, but does not set
4359  // tfSetfAuth on that trust line. alice attempts to create an
4360  // offer and again fails.
4361  //
4362  // 4. Finally, gw sets tsfSetAuth on the trust line authorizing
4363  // alice to own USD/gw. At this point alice successfully
4364  // creates and crosses an offer for USD/gw.
4365 
4366  using namespace jtx;
4367 
4368  Env env{*this, features};
4369 
4370  auto const gw = Account("gw");
4371  auto const alice = Account("alice");
4372  auto const bob = Account("bob");
4373  auto const gwUSD = gw["USD"];
4374  auto const aliceUSD = alice["USD"];
4375  auto const bobUSD = bob["USD"];
4376 
4377  env.fund(XRP(400000), gw, alice, bob);
4378  env.close();
4379 
4380  env(offer(alice, gwUSD(40), XRP(4000)));
4381  env.close();
4382 
4383  env.require(offers(alice, 1));
4384  env.require(balance(alice, gwUSD(none)));
4385  env(fset(gw, asfRequireAuth));
4386  env.close();
4387 
4388  env(trust(gw, bobUSD(100)), txflags(tfSetfAuth));
4389  env.close();
4390  env(trust(bob, gwUSD(100)));
4391  env.close();
4392 
4393  env(pay(gw, bob, gwUSD(50)));
4394  env.close();
4395  env.require(balance(bob, gwUSD(50)));
4396 
4397  // gw now requires authorization and bob has gwUSD(50). Let's see if
4398  // bob can cross alice's offer.
4399  //
4400  // o With Taker bob's offer should cross alice's.
4401  // o With FlowCross bob's offer shouldn't cross and alice's
4402  // unauthorized offer should be deleted.
4403  env(offer(bob, XRP(4000), gwUSD(40)));
4404  env.close();
4405  std::uint32_t const bobOfferSeq = env.seq(bob) - 1;
4406 
4407  bool const flowCross = features[featureFlowCross];
4408 
4409  env.require(offers(alice, 0));
4410  if (flowCross)
4411  {
4412  // alice's unauthorized offer is deleted & bob's offer not crossed.
4413  env.require(balance(alice, gwUSD(none)));
4414  env.require(offers(bob, 1));
4415  env.require(balance(bob, gwUSD(50)));
4416  }
4417  else
4418  {
4419  // alice's offer crosses bob's
4420  env.require(balance(alice, gwUSD(40)));
4421  env.require(offers(bob, 0));
4422  env.require(balance(bob, gwUSD(10)));
4423 
4424  // The rest of the test verifies FlowCross behavior.
4425  return;
4426  }
4427 
4428  // See if alice can create an offer without authorization. alice
4429  // should not be able to create the offer and bob's offer should be
4430  // untouched.
4431  env(offer(alice, gwUSD(40), XRP(4000)), ter(tecNO_LINE));
4432  env.close();
4433 
4434  env.require(offers(alice, 0));
4435  env.require(balance(alice, gwUSD(none)));
4436 
4437  env.require(offers(bob, 1));
4438  env.require(balance(bob, gwUSD(50)));
4439 
4440  // Set up a trust line for alice, but don't authorize it. alice
4441  // should still not be able to create an offer for USD/gw.
4442  env(trust(gw, aliceUSD(100)));
4443  env.close();
4444 
4445  env(offer(alice, gwUSD(40), XRP(4000)), ter(tecNO_AUTH));
4446  env.close();
4447 
4448  env.require(offers(alice, 0));
4449  env.require(balance(alice, gwUSD(0)));
4450 
4451  env.require(offers(bob, 1));
4452  env.require(balance(bob, gwUSD(50)));
4453 
4454  // Delete bob's offer so alice can create an offer without crossing.
4455  env(offer_cancel(bob, bobOfferSeq));
4456  env.close();
4457  env.require(offers(bob, 0));
4458 
4459  // Finally, set up an authorized trust line for alice. Now alice's
4460  // offer should succeed. Note that, since this is an offer rather
4461  // than a payment, alice does not need to set a trust line limit.
4462  env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
4463  env.close();
4464 
4465  env(offer(alice, gwUSD(40), XRP(4000)));
4466  env.close();
4467 
4468  env.require(offers(alice, 1));
4469 
4470  // Now bob creates his offer again. alice's offer should cross.
4471  env(offer(bob, XRP(4000), gwUSD(40)));
4472  env.close();
4473 
4474  env.require(offers(alice, 0));
4475  env.require(balance(alice, gwUSD(40)));
4476 
4477  env.require(offers(bob, 0));
4478  env.require(balance(bob, gwUSD(10)));
4479  }
4480 
4481  void
4483  {
4484  testcase("RippleConnect Smoketest payment flow");
4485  using namespace jtx;
4486 
4487  Env env{*this, features};
4488 
4489  // This test mimics the payment flow used in the Ripple Connect
4490  // smoke test. The players:
4491  // A USD gateway with hot and cold wallets
4492  // A EUR gateway with hot and cold walllets
4493  // A MM gateway that will provide offers from USD->EUR and EUR->USD
4494  // A path from hot US to cold EUR is found and then used to send
4495  // USD for EUR that goes through the market maker
4496 
4497  auto const hotUS = Account("hotUS");
4498  auto const coldUS = Account("coldUS");
4499  auto const hotEU = Account("hotEU");
4500  auto const coldEU = Account("coldEU");
4501  auto const mm = Account("mm");
4502 
4503  auto const USD = coldUS["USD"];
4504  auto const EUR = coldEU["EUR"];
4505 
4506  env.fund(XRP(100000), hotUS, coldUS, hotEU, coldEU, mm);
4507  env.close();
4508 
4509  // Cold wallets require trust but will ripple by default
4510  for (auto const& cold : {coldUS, coldEU})
4511  {
4512  env(fset(cold, asfRequireAuth));
4513  env(fset(cold, asfDefaultRipple));
4514  }
4515  env.close();
4516 
4517  // Each hot wallet trusts the related cold wallet for a large amount
4518  env(trust(hotUS, USD(10000000)), txflags(tfSetNoRipple));
4519  env(trust(hotEU, EUR(10000000)), txflags(tfSetNoRipple));
4520  // Market maker trusts both cold wallets for a large amount
4521  env(trust(mm, USD(10000000)), txflags(tfSetNoRipple));
4522  env(trust(mm, EUR(10000000)), txflags(tfSetNoRipple));
4523  env.close();
4524 
4525  // Gateways authorize the trustlines of hot and market maker
4526  env(trust(coldUS, USD(0), hotUS, tfSetfAuth));
4527  env(trust(coldEU, EUR(0), hotEU, tfSetfAuth));
4528  env(trust(coldUS, USD(0), mm, tfSetfAuth));
4529  env(trust(coldEU, EUR(0), mm, tfSetfAuth));
4530  env.close();
4531 
4532  // Issue currency from cold wallets to hot and market maker
4533  env(pay(coldUS, hotUS, USD(5000000)));
4534  env(pay(coldEU, hotEU, EUR(5000000)));
4535  env(pay(coldUS, mm, USD(5000000)));
4536  env(pay(coldEU, mm, EUR(5000000)));
4537  env.close();
4538 
4539  // MM places offers
4540  float const rate = 0.9f; // 0.9 USD = 1 EUR
4541  env(offer(mm, EUR(4000000 * rate), USD(4000000)),
4542  json(jss::Flags, tfSell));
4543 
4544  float const reverseRate = 1.0f / rate * 1.00101f;
4545  env(offer(mm, USD(4000000 * reverseRate), EUR(4000000)),
4546  json(jss::Flags, tfSell));
4547  env.close();
4548 
4549  // There should be a path available from hot US to cold EUR
4550  {
4551  Json::Value jvParams;
4552  jvParams[jss::destination_account] = coldEU.human();
4553  jvParams[jss::destination_amount][jss::issuer] = coldEU.human();
4554  jvParams[jss::destination_amount][jss::currency] = "EUR";
4555  jvParams[jss::destination_amount][jss::value] = 10;
4556  jvParams[jss::source_account] = hotUS.human();
4557 
4558  Json::Value const jrr{env.rpc(
4559  "json", "ripple_path_find", to_string(jvParams))[jss::result]};
4560 
4561  BEAST_EXPECT(jrr[jss::status] == "success");
4562  BEAST_EXPECT(
4563  jrr[jss::alternatives].isArray() &&
4564  jrr[jss::alternatives].size() > 0);
4565  }
4566  // Send the payment using the found path.
4567  env(pay(hotUS, coldEU, EUR(10)), sendmax(USD(11.1223326)));
4568  }
4569 
4570  void
4572  {
4573  testcase("Self Auth");
4574 
4575  using namespace jtx;
4576 
4577  Env env{*this, features};
4578 
4579  auto const gw = Account("gw");
4580  auto const alice = Account("alice");
4581  auto const gwUSD = gw["USD"];
4582  auto const aliceUSD = alice["USD"];
4583 
4584  env.fund(XRP(400000), gw, alice);
4585  env.close();
4586 
4587  // Test that gw can create an offer to buy gw's currency.
4588  env(offer(gw, gwUSD(40), XRP(4000)));
4589  env.close();
4590  std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
4591  env.require(offers(gw, 1));
4592 
4593  // Since gw has an offer out, gw should not be able to set RequireAuth.
4594  env(fset(gw, asfRequireAuth), ter(tecOWNERS));
4595  env.close();
4596 
4597  // Cancel gw's offer so we can set RequireAuth.
4598  env(offer_cancel(gw, gwOfferSeq));
4599  env.close();
4600  env.require(offers(gw, 0));
4601 
4602  // gw now requires authorization for holders of its IOUs
4603  env(fset(gw, asfRequireAuth));
4604  env.close();
4605 
4606  // The test behaves differently with or without DepositPreauth.
4607  bool const preauth = features[featureDepositPreauth];
4608 
4609  // Before DepositPreauth an account with lsfRequireAuth set could not
4610  // create an offer to buy their own currency. After DepositPreauth
4611  // they can.
4612  env(offer(gw, gwUSD(40), XRP(4000)),
4613  ter(preauth ? TER{tesSUCCESS} : TER{tecNO_LINE}));
4614  env.close();
4615 
4616  env.require(offers(gw, preauth ? 1 : 0));
4617 
4618  if (!preauth)
4619  // The rest of the test verifies DepositPreauth behavior.
4620  return;
4621 
4622  // Set up an authorized trust line and pay alice gwUSD 50.
4623  env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
4624  env(trust(alice, gwUSD(100)));
4625  env.close();
4626 
4627  env(pay(gw, alice, gwUSD(50)));
4628  env.close();
4629 
4630  env.require(balance(alice, gwUSD(50)));
4631 
4632  // alice's offer should cross gw's
4633  env(offer(alice, XRP(4000), gwUSD(40)));
4634  env.close();
4635 
4636  env.require(offers(alice, 0));
4637  env.require(balance(alice, gwUSD(10)));
4638 
4639  env.require(offers(gw, 0));
4640  }
4641 
4642  void
4644  {
4645  // Show that an offer who's issuer has been deleted cannot be crossed.
4646  using namespace jtx;
4647 
4648  testcase("Deleted offer issuer");
4649 
4650  auto trustLineExists = [](jtx::Env const& env,
4651  jtx::Account const& src,
4652  jtx::Account const& dst,
4653  Currency const& cur) -> bool {
4654  return bool(env.le(keylet::line(src, dst, cur)));
4655  };
4656 
4657  Account const alice("alice");
4658  Account const becky("becky");
4659  Account const carol("carol");
4660  Account const gw("gateway");
4661  auto const USD = gw["USD"];
4662  auto const BUX = alice["BUX"];
4663 
4664  Env env{*this, features};
4665 
4666  env.fund(XRP(10000), alice, becky, carol, noripple(gw));
4667  env.trust(USD(1000), becky);
4668  env(pay(gw, becky, USD(5)));
4669  env.close();
4670  BEAST_EXPECT(trustLineExists(env, gw, becky, USD.currency));
4671 
4672  // Make offers that produce USD and can be crossed two ways:
4673  // direct XRP -> USD
4674  // direct BUX -> USD
4675  env(offer(becky, XRP(2), USD(2)), txflags(tfPassive));
4676  std::uint32_t const beckyBuxUsdSeq{env.seq(becky)};
4677  env(offer(becky, BUX(3), USD(3)), txflags(tfPassive));
4678  env.close();
4679 
4680  // becky keeps the offers, but removes the trustline.
4681  env(pay(becky, gw, USD(5)));
4682  env.trust(USD(0), becky);
4683  env.close();
4684  BEAST_EXPECT(!trustLineExists(env, gw, becky, USD.currency));
4685  BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
4686  BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
4687 
4688  // Delete gw's account.
4689  {
4690  // The ledger sequence needs to far enough ahead of the account
4691  // sequence before the account can be deleted.
4692  int const delta =
4693  [&env, &gw, openLedgerSeq = env.current()->seq()]() -> int {
4694  std::uint32_t const gwSeq{env.seq(gw)};
4695  if (gwSeq + 255 > openLedgerSeq)
4696  return gwSeq - openLedgerSeq + 255;
4697  return 0;
4698  }();
4699 
4700  for (int i = 0; i < delta; ++i)
4701  env.close();
4702 
4703  // Account deletion has a high fee. Account for that.
4704  env(acctdelete(gw, alice),
4705  fee(drops(env.current()->fees().increment)));
4706  env.close();
4707 
4708  // Verify that gw's account root is gone from the ledger.
4709  BEAST_EXPECT(!env.closed()->exists(keylet::account(gw.id())));
4710  }
4711 
4712  // alice crosses becky's first offer. The offer create fails because
4713  // the USD issuer is not in the ledger.
4714  env(offer(alice, USD(2), XRP(2)), ter(tecNO_ISSUER));
4715  env.close();
4716  env.require(offers(alice, 0));
4717  BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
4718  BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
4719 
4720  // alice crosses becky's second offer. Again, the offer create fails
4721  // because the USD issuer is not in the ledger.
4722  env(offer(alice, USD(3), BUX(3)), ter(tecNO_ISSUER));
4723  env.require(offers(alice, 0));
4724  BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
4725  BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
4726 
4727  // Cancel becky's BUX -> USD offer so we can try auto-bridging.
4728  env(offer_cancel(becky, beckyBuxUsdSeq));
4729  env.close();
4730  BEAST_EXPECT(!isOffer(env, becky, BUX(3), USD(3)));
4731 
4732  // alice creates an offer that can be auto-bridged with becky's
4733  // remaining offer.
4734  env.trust(BUX(1000), carol);
4735  env(pay(alice, carol, BUX(2)));
4736 
4737  env(offer(alice, BUX(2), XRP(2)));
4738  env.close();
4739 
4740  // carol attempts the auto-bridge. Again, the offer create fails
4741  // because the USD issuer is not in the ledger.
4742  env(offer(carol, USD(2), BUX(2)), ter(tecNO_ISSUER));
4743  env.close();
4744  BEAST_EXPECT(isOffer(env, alice, BUX(2), XRP(2)));
4745  BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
4746  }
4747 
4748  void
4750  {
4751  testcase("Tick Size");
4752 
4753  using namespace jtx;
4754 
4755  // Try to set tick size out of range
4756  {
4757  Env env{*this, features};
4758  auto const gw = Account{"gateway"};
4759  env.fund(XRP(10000), gw);
4760 
4761  auto txn = noop(gw);
4762  txn[sfTickSize.fieldName] = Quality::minTickSize - 1;
4763  env(txn, ter(temBAD_TICK_SIZE));
4764 
4765  txn[sfTickSize.fieldName] = Quality::minTickSize;
4766  env(txn);
4767  BEAST_EXPECT((*env.le(gw))[sfTickSize] == Quality::minTickSize);
4768 
4769  txn = noop(gw);
4770  txn[sfTickSize.fieldName] = Quality::maxTickSize;
4771  env(txn);
4772  BEAST_EXPECT(!env.le(gw)->isFieldPresent(sfTickSize));
4773 
4774  txn = noop(gw);
4775  txn[sfTickSize.fieldName] = Quality::maxTickSize - 1;
4776  env(txn);
4777  BEAST_EXPECT((*env.le(gw))[sfTickSize] == Quality::maxTickSize - 1);
4778 
4779  txn = noop(gw);
4780  txn[sfTickSize.fieldName] = Quality::maxTickSize + 1;
4781  env(txn, ter(temBAD_TICK_SIZE));
4782 
4783  txn[sfTickSize.fieldName] = 0;
4784  env(txn);
4785  BEAST_EXPECT(!env.le(gw)->isFieldPresent(sfTickSize));
4786  }
4787 
4788  Env env{*this, features};
4789  auto const gw = Account{"gateway"};
4790  auto const alice = Account{"alice"};
4791  auto const XTS = gw["XTS"];
4792  auto const XXX = gw["XXX"];
4793 
4794  env.fund(XRP(10000), gw, alice);
4795 
4796  {
4797  // Gateway sets its tick size to 5
4798  auto txn = noop(gw);
4799  txn[sfTickSize.fieldName] = 5;
4800  env(txn);
4801  BEAST_EXPECT((*env.le(gw))[sfTickSize] == 5);
4802  }
4803 
4804  env(trust(alice, XTS(1000)));
4805  env(trust(alice, XXX(1000)));
4806 
4807  env(pay(gw, alice, alice["XTS"](100)));
4808  env(pay(gw, alice, alice["XXX"](100)));
4809 
4810  env(offer(alice, XTS(10), XXX(30)));
4811  env(offer(alice, XTS(30), XXX(10)));
4812  env(offer(alice, XTS(10), XXX(30)), json(jss::Flags, tfSell));
4813  env(offer(alice, XTS(30), XXX(10)), json(jss::Flags, tfSell));
4814 
4816  forEachItem(
4817  *env.current(), alice, [&](std::shared_ptr<SLE const> const& sle) {
4818  if (sle->getType() == ltOFFER)
4819  offers.emplace(
4820  (*sle)[sfSequence],
4821  std::make_pair(
4822  (*sle)[sfTakerPays], (*sle)[sfTakerGets]));
4823  });
4824 
4825  // first offer
4826  auto it = offers.begin();
4827  BEAST_EXPECT(it != offers.end());
4828  BEAST_EXPECT(
4829  it->second.first == XTS(10) && it->second.second < XXX(30) &&
4830  it->second.second > XXX(29.9994));
4831 
4832  // second offer
4833  ++it;
4834  BEAST_EXPECT(it != offers.end());
4835  BEAST_EXPECT(
4836  it->second.first == XTS(30) && it->second.second == XXX(10));
4837 
4838  // third offer
4839  ++it;
4840  BEAST_EXPECT(it != offers.end());
4841  BEAST_EXPECT(
4842  it->second.first == XTS(10.0002) && it->second.second == XXX(30));
4843 
4844  // fourth offer
4845  // exact TakerPays is XTS(1/.033333)
4846  ++it;
4847  BEAST_EXPECT(it != offers.end());
4848  BEAST_EXPECT(
4849  it->second.first == XTS(30) && it->second.second == XXX(10));
4850 
4851  BEAST_EXPECT(++it == offers.end());
4852  }
4853 
4854  // Helper function that returns offers on an account sorted by sequence.
4857  {
4859  offersOnAccount(env, acct)};
4860  std::sort(
4861  offers.begin(),
4862  offers.end(),
4863  [](std::shared_ptr<SLE const> const& rhs,
4864  std::shared_ptr<SLE const> const& lhs) {
4865  return (*rhs)[sfSequence] < (*lhs)[sfSequence];
4866  });
4867  return offers;
4868  }
4869 
4870  void
4872  {
4873  testcase("Ticket Offers");
4874 
4875  using namespace jtx;
4876 
4877  // Two goals for this test.
4878  //
4879  // o Verify that offers can be created using tickets.
4880  //
4881  // o Show that offers in the _same_ order book remain in
4882  // chronological order regardless of sequence/ticket numbers.
4883  Env env{*this, features};
4884  auto const gw = Account{"gateway"};
4885  auto const alice = Account{"alice"};
4886  auto const bob = Account{"bob"};
4887  auto const USD = gw["USD"];
4888 
4889  env.fund(XRP(10000), gw, alice, bob);
4890  env.close();
4891 
4892  env(trust(alice, USD(1000)));
4893  env(trust(bob, USD(1000)));
4894  env.close();
4895 
4896  env(pay(gw, alice, USD(200)));
4897  env.close();
4898 
4899  // Create four offers from the same account with identical quality
4900  // so they go in the same order book. Each offer goes in a different
4901  // ledger so the chronology is clear.
4902  std::uint32_t const offerId_0{env.seq(alice)};
4903  env(offer(alice, XRP(50), USD(50)));
4904  env.close();
4905 
4906  // Create two tickets.
4907  std::uint32_t const ticketSeq{env.seq(alice) + 1};
4908  env(ticket::create(alice, 2));
4909  env.close();
4910 
4911  // Create another sequence-based offer.
4912  std::uint32_t const offerId_1{env.seq(alice)};
4913  BEAST_EXPECT(offerId_1 == offerId_0 + 4);
4914  env(offer(alice, XRP(50), USD(50)));
4915  env.close();
4916 
4917  // Create two ticket based offers in reverse order.
4918  std::uint32_t const offerId_2{ticketSeq + 1};
4919  env(offer(alice, XRP(50), USD(50)), ticket::use(offerId_2));
4920  env.close();
4921 
4922  // Create the last offer.
4923  std::uint32_t const offerId_3{ticketSeq};
4924  env(offer(alice, XRP(50), USD(50)), ticket::use(offerId_3));
4925  env.close();
4926 
4927  // Verify that all of alice's offers are present.
4928  {
4929  auto offers = sortedOffersOnAccount(env, alice);
4930  BEAST_EXPECT(offers.size() == 4);
4931  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_0);
4932  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_3);
4933  BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerId_2);
4934  BEAST_EXPECT(offers[3]->getFieldU32(sfSequence) == offerId_1);
4935  env.require(balance(alice, USD(200)));
4936  env.require(owners(alice, 5));
4937  }
4938 
4939  // Cross alice's first offer.
4940  env(offer(bob, USD(50), XRP(50)));
4941  env.close();
4942 
4943  // Verify that the first offer alice created was consumed.
4944  {
4945  auto offers = sortedOffersOnAccount(env, alice);
4946  BEAST_EXPECT(offers.size() == 3);
4947  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
4948  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_2);
4949  BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerId_1);
4950  }
4951 
4952  // Cross alice's second offer.
4953  env(offer(bob, USD(50), XRP(50)));
4954  env.close();
4955 
4956  // Verify that the second offer alice created was consumed.
4957  {
4958  auto offers = sortedOffersOnAccount(env, alice);
4959  BEAST_EXPECT(offers.size() == 2);
4960  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
4961  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_2);
4962  }
4963 
4964  // Cross alice's third offer.
4965  env(offer(bob, USD(50), XRP(50)));
4966  env.close();
4967 
4968  // Verify that the third offer alice created was consumed.
4969  {
4970  auto offers = sortedOffersOnAccount(env, alice);
4971  BEAST_EXPECT(offers.size() == 1);
4972  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
4973  }
4974 
4975  // Cross alice's last offer.
4976  env(offer(bob, USD(50), XRP(50)));
4977  env.close();
4978 
4979  // Verify that the third offer alice created was consumed.
4980  {
4981  auto offers = sortedOffersOnAccount(env, alice);
4982  BEAST_EXPECT(offers.size() == 0);
4983  }
4984  env.require(balance(alice, USD(0)));
4985  env.require(owners(alice, 1));
4986  env.require(balance(bob, USD(200)));
4987  env.require(owners(bob, 1));
4988  }
4989 
4990  void
4992  {
4993  testcase("Ticket Cancel Offers");
4994 
4995  using namespace jtx;
4996 
4997  // Verify that offers created with or without tickets can be canceled
4998  // by transactions with or without tickets.
4999  Env env{*this, features};
5000  auto const gw = Account{"gateway"};
5001  auto const alice = Account{"alice"};
5002  auto const USD = gw["USD"];
5003 
5004  env.fund(XRP(10000), gw, alice);
5005  env.close();
5006 
5007  env(trust(alice, USD(1000)));
5008  env.close();
5009  env.require(owners(alice, 1), tickets(alice, 0));
5010 
5011  env(pay(gw, alice, USD(200)));
5012  env.close();
5013 
5014  // Create the first of four offers using a sequence.
5015  std::uint32_t const offerSeqId_0{env.seq(alice)};
5016  env(offer(alice, XRP(50), USD(50)));
5017  env.close();
5018  env.require(owners(alice, 2), tickets(alice, 0));
5019 
5020  // Create four tickets.
5021  std::uint32_t const ticketSeq{env.seq(alice) + 1};
5022  env(ticket::create(alice, 4));
5023  env.close();
5024  env.require(owners(alice, 6), tickets(alice, 4));
5025 
5026  // Create the second (also sequence-based) offer.
5027  std::uint32_t const offerSeqId_1{env.seq(alice)};
5028  BEAST_EXPECT(offerSeqId_1 == offerSeqId_0 + 6);
5029  env(offer(alice, XRP(50), USD(50)));
5030  env.close();
5031 
5032  // Create the third (ticket-based) offer.
5033  std::uint32_t const offerTixId_0{ticketSeq + 1};
5034  env(offer(alice, XRP(50), USD(50)), ticket::use(offerTixId_0));
5035  env.close();
5036 
5037  // Create the last offer.
5038  std::uint32_t const offerTixId_1{ticketSeq};
5039  env(offer(alice, XRP(50), USD(50)), ticket::use(offerTixId_1));
5040  env.close();
5041 
5042  // Verify that all of alice's offers are present.
5043  {
5044  auto offers = sortedOffersOnAccount(env, alice);
5045  BEAST_EXPECT(offers.size() == 4);
5046  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerSeqId_0);
5047  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerTixId_1);
5048  BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerTixId_0);
5049  BEAST_EXPECT(offers[3]->getFieldU32(sfSequence) == offerSeqId_1);
5050  env.require(balance(alice, USD(200)));
5051  env.require(owners(alice, 7));
5052  }
5053 
5054  // Use a ticket to cancel an offer created with a sequence.
5055  env(offer_cancel(alice, offerSeqId_0), ticket::use(ticketSeq + 2));
5056  env.close();
5057 
5058  // Verify that offerSeqId_0 was canceled.
5059  {
5060  auto offers = sortedOffersOnAccount(env, alice);
5061  BEAST_EXPECT(offers.size() == 3);
5062  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerTixId_1);
5063  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerTixId_0);
5064  BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerSeqId_1);
5065  }
5066 
5067  // Use a ticket to cancel an offer created with a ticket.
5068  env(offer_cancel(alice, offerTixId_0), ticket::use(ticketSeq + 3));
5069  env.close();
5070 
5071  // Verify that offerTixId_0 was canceled.
5072  {
5073  auto offers = sortedOffersOnAccount(env, alice);
5074  BEAST_EXPECT(offers.size() == 2);
5075  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerTixId_1);
5076  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerSeqId_1);
5077  }
5078 
5079  // All of alice's tickets should now be used up.
5080  env.require(owners(alice, 3), tickets(alice, 0));
5081 
5082  // Use a sequence to cancel an offer created with a ticket.
5083  env(offer_cancel(alice, offerTixId_1));
5084  env.close();
5085 
5086  // Verify that offerTixId_1 was canceled.
5087  {
5088  auto offers = sortedOffersOnAccount(env, alice);
5089  BEAST_EXPECT(offers.size() == 1);
5090  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerSeqId_1);
5091  }
5092 
5093  // Use a sequence to cancel an offer created with a sequence.
5094  env(offer_cancel(alice, offerSeqId_1));
5095  env.close();
5096 
5097  // Verify that offerSeqId_1 was canceled.
5098  // All of alice's tickets should now be used up.
5099  env.require(owners(alice, 1), tickets(alice, 0), offers(alice, 0));
5100  }
5101 
5102  void
5104  {
5105  // An assert was falsely triggering when computing rates for offers.
5106  // This unit test would trigger that assert (which has been removed).
5107  testcase("incorrect assert fixed");
5108  using namespace jtx;
5109 
5110  Env env{*this};
5111  auto const alice = Account("alice");
5112  auto const USD = alice["USD"];
5113 
5114  env.fund(XRP(10000), alice);
5115  env.close();
5116  env(offer(alice, XRP(100000000000), USD(100000000)));
5117  pass();
5118  }
5119 
5120  void
5122  {
5123  testCanceledOffer(features);
5124  testRmFundedOffer(features);
5125  testTinyPayment(features);
5126  testXRPTinyPayment(features);
5127  testEnforceNoRipple(features);
5128  testInsufficientReserve(features);
5129  testFillModes(features);
5130  testMalformed(features);
5131  testExpiration(features);
5132  testUnfundedCross(features);
5133  testSelfCross(false, features);
5134  testSelfCross(true, features);
5135  testNegativeBalance(features);
5136  testOfferCrossWithXRP(true, features);
5137  testOfferCrossWithXRP(false, features);
5139  testOfferAcceptThenCancel(features);
5140  testOfferCancelPastAndFuture(features);
5141  testCurrencyConversionEntire(features);
5144  testCrossCurrencyStartXRP(features);
5145  testCrossCurrencyEndXRP(features);
5146  testCrossCurrencyBridged(features);
5147  testBridgedSecondLegDry(features);
5148  testOfferFeesConsumeFunds(features);
5149  testOfferCreateThenCross(features);
5150  testSellFlagBasic(features);
5151  testSellFlagExceedLimit(features);
5152  testGatewayCrossCurrency(features);
5153  testPartialCross(features);
5154  testXRPDirectCross(features);
5155  testDirectCross(features);
5156  testBridgedCross(features);
5157  testSellOffer(features);
5158  testSellWithFillOrKill(features);
5159  testTransferRateOffer(features);
5160  testSelfCrossOffer(features);
5161  testSelfIssueOffer(features);
5162  testBadPathAssert(features);
5163  testDirectToDirectPath(features);
5164  testSelfCrossLowQualityOffer(features);
5165  testOfferInScaling(features);
5168  testTinyOffer(features);
5169  testSelfPayXferFeeOffer(features);
5170  testSelfPayUnlimitedFunds(features);
5171  testRequireAuth(features);
5172  testMissingAuth(features);
5173  testRCSmoketest(features);
5174  testSelfAuth(features);
5175  testDeletedOfferIssuer(features);
5176  testTickSize(features);
5177  testTicketOffer(features);
5178  testTicketCancelOffer(features);
5181  }
5182 
5183  void
5184  run() override
5185  {
5186  using namespace jtx;
5188  FeatureBitset const flowCross{featureFlowCross};
5189  FeatureBitset const takerDryOffer{fixTakerDryOfferRemoval};
5190  FeatureBitset const rmSmallIncreasedQOffers{fixRmSmallIncreasedQOffers};
5191  FeatureBitset const immediateOfferKilled{featureImmediateOfferKilled};
5192 
5193  testAll(all - takerDryOffer - immediateOfferKilled);
5194  testAll(all - flowCross - takerDryOffer - immediateOfferKilled);
5195  testAll(all - flowCross - immediateOfferKilled);
5196  testAll(all - rmSmallIncreasedQOffers - immediateOfferKilled);
5197  testAll(all);
5198  testFalseAssert();
5199  }
5200 };
5201 
5203 {
5204  void
5205  run() override
5206  {
5207  using namespace jtx;
5209  FeatureBitset const flowCross{featureFlowCross};
5210  FeatureBitset const f1513{fix1513};
5211  FeatureBitset const immediateOfferKilled{featureImmediateOfferKilled};
5212  FeatureBitset const takerDryOffer{fixTakerDryOfferRemoval};
5213 
5214  testAll(all - flowCross - f1513 - immediateOfferKilled);
5215  testAll(all - flowCross - immediateOfferKilled);
5216  testAll(all - immediateOfferKilled);
5217  testAll(all);
5218 
5219  testAll(all - flowCross - takerDryOffer);
5220  }
5221 };
5222 
5224 BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Offer_manual, tx, ripple, 20);
5225 
5226 } // namespace test
5227 } // namespace ripple
ripple::badCurrency
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
Definition: UintTypes.cpp:135
ripple::test::Offer_test::testCrossCurrencyEndXRP
void testCrossCurrencyEndXRP(FeatureBitset features)
Definition: Offer_test.cpp:1852
ripple::tecUNFUNDED_OFFER
@ tecUNFUNDED_OFFER
Definition: TER.h:251
ripple::test::Offer_test::testOfferInScaling
void testOfferInScaling(FeatureBitset features)
Definition: Offer_test.cpp:3768
ripple::test::jtx::json
Inject raw JSON.
Definition: jtx_json.h:31
ripple::test::Offer_test::testCurrencyConversionIntoDebt
void testCurrencyConversionIntoDebt(FeatureBitset features)
Definition: Offer_test.cpp:1697
ripple::test::Offer_test::testGatewayCrossCurrency
void testGatewayCrossCurrency(FeatureBitset features)
Definition: Offer_test.cpp:2231
ripple::test::jtx::noop
Json::Value noop(Account const &account)
The null transaction.
Definition: noop.h:31
ripple::STAmount::negate
void negate()
Definition: STAmount.h:402
ripple::test::Offer_test::reserve
XRPAmount reserve(jtx::Env &env, std::uint32_t count)
Definition: Offer_test.cpp:33
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
ripple::test::jtx::dropsPerXRP
constexpr XRPAmount dropsPerXRP
Definition: amount.h:67
ripple::test::Offer_test::testCurrencyConversionEntire
void testCurrencyConversionEntire(FeatureBitset features)
Definition: Offer_test.cpp:1644
ripple::Issue
A currency issued by an account.
Definition: Issue.h:34
std::string
STL class.
ripple::temBAD_OFFER
@ temBAD_OFFER
Definition: TER.h:93
ripple::test::Offer_test::testBridgedCross
void testBridgedCross(FeatureBitset features)
Definition: Offer_test.cpp:2692
std::shared_ptr
STL class.
ripple::test::Offer_test::ledgerEntryRoot
static auto ledgerEntryRoot(jtx::Env &env, jtx::Account const &acct)
Definition: Offer_test.cpp:71
ripple::test::Offer_test::testOfferThresholdWithReducedFunds
void testOfferThresholdWithReducedFunds(FeatureBitset features)
Definition: Offer_test.cpp:3867
ripple::test::Offer_test::verifyDefaultTrustline
void verifyDefaultTrustline(jtx::Env &env, jtx::Account const &account, jtx::PrettyAmount const &expectBalance)
Definition: Offer_test.cpp:2308
ripple::test::Offer_manual_test
Definition: Offer_test.cpp:5202
ripple::test::jtx::drops
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Definition: amount.h:241
ripple::temBAD_CURRENCY
@ temBAD_CURRENCY
Definition: TER.h:88
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:347
ripple::test::jtx::ter
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:33
ripple::tecOWNERS
@ tecOWNERS
Definition: TER.h:265
ripple::test::jtx::owners
Match the number of items in the account's owner directory.
Definition: owners.h:69
ripple::test::Offer_test::sortedOffersOnAccount
static std::vector< std::shared_ptr< SLE const > > sortedOffersOnAccount(jtx::Env &env, jtx::Account const &acct)
Definition: Offer_test.cpp:4856
ripple::test::Offer_test::testSelfCross
void testSelfCross(bool use_partner, FeatureBitset features)
Definition: Offer_test.cpp:1302
ripple::test::jtx::Env::require
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:466
ripple::test::jtx::Env::closed
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition: Env.cpp:115
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::featureDepositPreauth
const uint256 featureDepositPreauth
ripple::test::Offer_test::testTicketOffer
void testTicketOffer(FeatureBitset features)
Definition: Offer_test.cpp:4871
ripple::sfSequence
const SF_UINT32 sfSequence
ripple::TxSearched::all
@ all
ripple::test::jtx::balance
A balance matches.
Definition: balance.h:38
std::vector
STL class.
ripple::test::Offer_test::run
void run() override
Definition: Offer_test.cpp:5184
ripple::test::Offer_test::testEnforceNoRipple
void testEnforceNoRipple(FeatureBitset features)
Definition: Offer_test.cpp:668
ripple::test::jtx::trust
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition: trust.cpp:30
ripple::SField::fieldName
const std::string fieldName
Definition: SField.h:132
ripple::test::Offer_test::testCanceledOffer
void testCanceledOffer(FeatureBitset features)
Definition: Offer_test.cpp:170
ripple::tfSetNoRipple
constexpr std::uint32_t tfSetNoRipple
Definition: TxFlags.h:110
ripple::STAmount::getText
std::string getText() const override
Definition: STAmount.cpp:571
ripple::test::Offer_test::testSelfCrossOffer
void testSelfCrossOffer(FeatureBitset features)
Definition: Offer_test.cpp:3553
ripple::test::Offer_test::getBookOffers
static auto getBookOffers(jtx::Env &env, Issue const &taker_pays, Issue const &taker_gets)
Definition: Offer_test.cpp:94
ripple::test::jtx::offer_cancel
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition: offer.cpp:45
ripple::test::jtx::require
Check a set of conditions.
Definition: require.h:63
ripple::Issue::currency
Currency currency
Definition: Issue.h:37
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:104
ripple::test::Path
Definition: PathSet.h:92
ripple::test::jtx::Account::human
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:113
ripple::test::Offer_test::testRmSmallIncreasedQOffersXRP
void testRmSmallIncreasedQOffersXRP(FeatureBitset features)
Definition: Offer_test.cpp:357
std::distance
T distance(T... args)
ripple::test::Offer_test::testSelfPayUnlimitedFunds
void testSelfPayUnlimitedFunds(FeatureBitset features)
Definition: Offer_test.cpp:4136
ripple::test::Offer_test::testSelfCrossLowQualityOffer
void testSelfCrossLowQualityOffer(FeatureBitset features)
Definition: Offer_test.cpp:3732
ripple::tfPassive
constexpr std::uint32_t tfPassive
Definition: TxFlags.h:94
ripple::test::Offer_test::ledgerEntryState
static auto ledgerEntryState(jtx::Env &env, jtx::Account const &acct_a, jtx::Account const &acct_b, std::string const &currency)
Definition: Offer_test.cpp:54
ripple::test::Offer_test::testRCSmoketest
void testRCSmoketest(FeatureBitset features)
Definition: Offer_test.cpp:4482
ripple::test::Offer_test::testMissingAuth
void testMissingAuth(FeatureBitset features)
Definition: Offer_test.cpp:4344
ripple::test::Offer_test::testFalseAssert
void testFalseAssert()
Definition: Offer_test.cpp:5103
ripple::test::Offer_test::testUnfundedCross
void testUnfundedCross(FeatureBitset features)
Definition: Offer_test.cpp:1248
ripple::test::Offer_test::testPartialCross
void testPartialCross(FeatureBitset features)
Definition: Offer_test.cpp:2339
ripple::temBAD_PATH
@ temBAD_PATH
Definition: TER.h:94
ripple::test::jtx::qualityInPercent
Sets the QualityIn on a trust JTx.
Definition: quality.h:45
ripple::test::Offer_test::testRequireAuth
void testRequireAuth(FeatureBitset features)
Definition: Offer_test.cpp:4293
std::sort
T sort(T... args)
ripple::test::jtx::Env::trust
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:259
ripple::test::BEAST_DEFINE_TESTSUITE_PRIO
BEAST_DEFINE_TESTSUITE_PRIO(AccountDelete, app, ripple, 2)
ripple::test::Offer_test::testCrossCurrencyBridged
void testCrossCurrencyBridged(FeatureBitset features)
Definition: Offer_test.cpp:1899
ripple::tecKILLED
@ tecKILLED
Definition: TER.h:283
ripple::sfExpiration
const SF_UINT32 sfExpiration
ripple::test::jtx::Account::id
AccountID id() const
Returns the Account ID.
Definition: Account.h:106
ripple::base_uint< 160, detail::CurrencyTag >
ripple::sfTakerPays
const SF_AMOUNT sfTakerPays
ripple::test::jtx::ticket::use
Set a ticket sequence on a JTx.
Definition: ticket.h:47
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:109
ripple::sfLowLimit
const SF_AMOUNT sfLowLimit
ripple::ltOFFER
@ ltOFFER
A ledger object which describes an offer on the DEX.
Definition: LedgerFormats.h:92
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
ripple::test::Offer_test::testOfferInScalingWithXferRate
void testOfferInScalingWithXferRate(FeatureBitset features)
Definition: Offer_test.cpp:3815
ripple::test::BEAST_DEFINE_TESTSUITE_MANUAL_PRIO
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(CrossingLimits, tx, ripple, 10)
ripple::tfPartialPayment
constexpr std::uint32_t tfPartialPayment
Definition: TxFlags.h:103
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:133
ripple::test::Offer_test::testSellFlagExceedLimit
void testSellFlagExceedLimit(FeatureBitset features)
Definition: Offer_test.cpp:2187
ripple::fix1513
const uint256 fix1513
ripple::test::Offer_test::testCrossCurrencyStartXRP
void testCrossCurrencyStartXRP(FeatureBitset features)
Definition: Offer_test.cpp:1811
ripple::JsonOptions::none
@ none
ripple::TERSubset< CanCvtToTER >
ripple::test::jtx::sendmax
Sets the SendMax on a JTx.
Definition: sendmax.h:31
ripple::test::Offer_test::testTinyPayment
void testTinyPayment(FeatureBitset features)
Definition: Offer_test.cpp:251
ripple::test::PathSet
Definition: PathSet.h:165
ripple::temBAD_SEQUENCE
@ temBAD_SEQUENCE
Definition: TER.h:102
ripple::test::Offer_test::testOfferCrossWithXRP
void testOfferCrossWithXRP(bool reverse_order, FeatureBitset features)
Definition: Offer_test.cpp:1501
ripple::test::jtx::fset
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition: flags.cpp:28
ripple::fixTakerDryOfferRemoval
const uint256 fixTakerDryOfferRemoval
std::to_string
T to_string(T... args)
ripple::test::jtx::txflags
Set the flags on a JTx.
Definition: txflags.h:30
ripple::test::jtx::paths
Set Paths, SendMax on a JTx.
Definition: paths.h:32
ripple::test::jtx::Env::close
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:121
ripple::test::Offer_test::testXRPTinyPayment
void testXRPTinyPayment(FeatureBitset features)
Definition: Offer_test.cpp:285
ripple::STAmount
Definition: STAmount.h:45
ripple::test::Offer_test::lastClose
std::uint32_t lastClose(jtx::Env &env)
Definition: Offer_test.cpp:39
ripple::test::isOffer
bool isOffer(jtx::Env &env, jtx::Account const &account, STAmount const &takerPays, STAmount const &takerGets)
An offer exists.
Definition: PathSet.h:71
ripple::sfTakerGets
const SF_AMOUNT sfTakerGets
ripple::sfTickSize
const SF_UINT8 sfTickSize
ripple::test::Offer_test::xrpMinusFee
static auto xrpMinusFee(jtx::Env const &env, std::int64_t xrpAmount) -> jtx::PrettyAmount
Definition: Offer_test.cpp:45
ripple::test::Offer_test::testCurrencyConversionInParts
void testCurrencyConversionInParts(FeatureBitset features)
Definition: Offer_test.cpp:1726
ripple::test::jtx::path
Add a path.
Definition: paths.h:55
ripple::test::Offer_test::testOfferCancelPastAndFuture
void testOfferCancelPastAndFuture(FeatureBitset features)
Definition: Offer_test.cpp:1619
ripple::test::jtx::supported_amendments
FeatureBitset supported_amendments()
Definition: Env.h:70
std::uint32_t
ripple::sfHighLimit
const SF_AMOUNT sfHighLimit
ripple::tecPATH_PARTIAL
@ tecPATH_PARTIAL
Definition: TER.h:249
ripple::keylet::line
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition: Indexes.cpp:193
std::remove_if
T remove_if(T... args)
ripple::test::jtx::Env::seq
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition: Env.cpp:207
std::map
STL class.
ripple::temREDUNDANT
@ temREDUNDANT
Definition: TER.h:110
ripple::test::jtx::qualityOutPercent
Sets the QualityOut on a trust JTx as a percentage.
Definition: quality.h:73
ripple::fixRmSmallIncreasedQOffers
const uint256 fixRmSmallIncreasedQOffers
ripple::test::Offer_test::testSellOffer
void testSellOffer(FeatureBitset features)
Definition: Offer_test.cpp:2790
ripple::tfFillOrKill
constexpr std::uint32_t tfFillOrKill
Definition: TxFlags.h:96
ripple::test::jtx::fee
Set the fee on a JTx.
Definition: fee.h:35
ripple::test::Offer_test::offersOnAccount
static std::vector< std::shared_ptr< SLE const > > offersOnAccount(jtx::Env &env, jtx::Account account)
Definition: Offer_test.cpp:847
ripple::fix1578
const uint256 fix1578
ripple::test::Offer_test::testInsufficientReserve
void testInsufficientReserve(FeatureBitset features)
Definition: Offer_test.cpp:742
ripple::getJson
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
Definition: LedgerToJson.cpp:296
ripple::generateSeed
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
Definition: Seed.cpp:69
ripple::test::Offer_test::testNegativeBalance
void testNegativeBalance(FeatureBitset features)
Definition: Offer_test.cpp:1418
ripple::test::Offer_test::testDirectToDirectPath
void testDirectToDirectPath(FeatureBitset features)
Definition: Offer_test.cpp:3679
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::tfSell
constexpr std::uint32_t tfSell
Definition: TxFlags.h:97
ripple::test::Offer_test::testSelfIssueOffer
void testSelfIssueOffer(FeatureBitset features)
Definition: Offer_test.cpp:3561
ripple::featureImmediateOfferKilled
const uint256 featureImmediateOfferKilled
ripple::test::jtx::flags
Match set account flags.
Definition: flags.h:111
ripple::test::jtx::noripple
std::array< Account, 1+sizeof...(Args)> noripple(Account const &account, Args const &... args)
Designate accounts as no-ripple in Env::fund.
Definition: Env.h:64
ripple::sfLedgerEntryType
const SF_UINT16 sfLedgerEntryType
ripple::test::jtx::IOU
Converts to IOU Issue or STAmount.
Definition: amount.h:291
ripple::test::jtx::pay
Json::Value pay(Account const &account, Account const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
ripple::tecNO_LINE
@ tecNO_LINE
Definition: TER.h:268
ripple::tecEXPIRED
@ tecEXPIRED
Definition: TER.h:281
ripple::test::Offer_test::testOfferFeesConsumeFunds
void testOfferFeesConsumeFunds(FeatureBitset features)
Definition: Offer_test.cpp:2036
ripple::tfSetfAuth
constexpr std::uint32_t tfSetfAuth
Definition: TxFlags.h:109
ripple::test::Offer_manual_test::run
void run() override
Definition: Offer_test.cpp:5205
ripple::test::Offer_test::testAll
void testAll(FeatureBitset features)
Definition: Offer_test.cpp:5121
ripple::asfRequireAuth
constexpr std::uint32_t asfRequireAuth
Definition: TxFlags.h:75
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:228
ripple::test::jtx::Env::le
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:216
ripple::tecNO_ISSUER
@ tecNO_ISSUER
Definition: TER.h:266
ripple::test::Offer_test
Definition: Offer_test.cpp:30
ripple::test::Offer_test::testTinyOffer
void testTinyOffer(FeatureBitset features)
Definition: Offer_test.cpp:3934
ripple::asfDefaultRipple
constexpr std::uint32_t asfDefaultRipple
Definition: TxFlags.h:81
ripple::test::Offer_test::testOfferCreateThenCross
void testOfferCreateThenCross(FeatureBitset features)
Definition: Offer_test.cpp:2090
ripple::fixUniversalNumber
const uint256 fixUniversalNumber
ripple::test::Offer_test::testXRPDirectCross
void testXRPDirectCross(FeatureBitset features)
Definition: Offer_test.cpp:2497
ripple::sfBalance
const SF_AMOUNT sfBalance
ripple::FeatureBitset
Definition: Feature.h:113
ripple::test::jtx::acctdelete
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
Definition: acctdelete.cpp:29
ripple::test::Offer_test::testRmSmallIncreasedQOffersIOU
void testRmSmallIncreasedQOffersIOU(FeatureBitset features)
Definition: Offer_test.cpp:505
ripple::test::Offer_test::testSelfCrossOffer2
void testSelfCrossOffer2(FeatureBitset features)
Definition: Offer_test.cpp:3441
ripple::test::makeWSClient
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Definition: WSClient.cpp:300
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:261
ripple::xrpIssue
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:95
ripple::test::Offer_test::testDirectCross
void testDirectCross(FeatureBitset features)
Definition: Offer_test.cpp:2574
ripple::forEachItem
void forEachItem(ReadView const &view, Keylet const &root, std::function< void(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items in the given directory.
Definition: View.cpp:367
ripple::test::jtx::PrettyAmount::value
STAmount const & value() const
Definition: amount.h:124
std::size_t
ripple::test::Offer_test::testSelfPayXferFeeOffer
void testSelfPayXferFeeOffer(FeatureBitset features)
Definition: Offer_test.cpp:3982
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::test::jtx::Account
Immutable cryptographic account descriptor.
Definition: Account.h:37
ripple::test::Offer_test::testBadPathAssert
void testBadPathAssert(FeatureBitset features)
Definition: Offer_test.cpp:3606
ripple::featureOwnerPaysFee
const uint256 featureOwnerPaysFee
ripple::test::Offer_test::testSellWithFillOrKill
void testSellWithFillOrKill(FeatureBitset features)
Definition: Offer_test.cpp:2978
ripple::test::Offer_test::testExpiration
void testExpiration(FeatureBitset features)
Definition: Offer_test.cpp:1170
ripple::test::Offer_test::testDeletedOfferIssuer
void testDeletedOfferIssuer(FeatureBitset features)
Definition: Offer_test.cpp:4643
ripple::test::Offer_test::testTickSize
void testTickSize(FeatureBitset features)
Definition: Offer_test.cpp:4749
ripple::temBAD_TICK_SIZE
@ temBAD_TICK_SIZE
Definition: TER.h:116
ripple::test::Offer_test::testTicketCancelOffer
void testTicketCancelOffer(FeatureBitset features)
Definition: Offer_test.cpp:4991
ripple::test::Offer_test::testTransferRateOffer
void testTransferRateOffer(FeatureBitset features)
Definition: Offer_test.cpp:3063
ripple::tecNO_AUTH
@ tecNO_AUTH
Definition: TER.h:267
ripple::test::Offer_test::testOfferCrossWithLimitOverride
void testOfferCrossWithLimitOverride(FeatureBitset features)
Definition: Offer_test.cpp:1555
ripple::temBAD_EXPIRATION
@ temBAD_EXPIRATION
Definition: TER.h:89
ripple::test::Offer_test::testSelfCrossOffer1
void testSelfCrossOffer1(FeatureBitset features)
Definition: Offer_test.cpp:3371
ripple::test::Offer_test::testSelfAuth
void testSelfAuth(FeatureBitset features)
Definition: Offer_test.cpp:4571
ripple::featureFlowCross
const uint256 featureFlowCross
ripple::test::Offer_test::testSellFlagBasic
void testSellFlagBasic(FeatureBitset features)
Definition: Offer_test.cpp:2145
ripple::tecINSUF_RESERVE_OFFER
@ tecINSUF_RESERVE_OFFER
Definition: TER.h:256
ripple::test::Offer_test::testRmFundedOffer
void testRmFundedOffer(FeatureBitset features)
Definition: Offer_test.cpp:110
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:222
ripple::TOffer
Definition: Offer.h:49
ripple::test::Offer_test::testOfferAcceptThenCancel
void testOfferAcceptThenCancel(FeatureBitset features)
Definition: Offer_test.cpp:1595
ripple::tfImmediateOrCancel
constexpr std::uint32_t tfImmediateOrCancel
Definition: TxFlags.h:95
ripple::test::jtx::Env::current
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:300
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
ripple::Issue::account
AccountID account
Definition: Issue.h:38
ripple::test::jtx::Env::rpc
Json::Value rpc(std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition: Env.h:687
ripple::tfNoRippleDirect
constexpr std::uint32_t tfNoRippleDirect
Definition: TxFlags.h:102
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::test::jtx::PrettyAmount
Represents an XRP or IOU quantity This customizes the string conversion and supports XRP conversions ...
Definition: amount.h:73
ripple::test::Offer_test::testMalformed
void testMalformed(FeatureBitset features)
Definition: Offer_test.cpp:1093
ripple::test::Offer_test::ledgerEntryOffer
static auto ledgerEntryOffer(jtx::Env &env, jtx::Account const &acct, std::uint32_t offer_seq)
Definition: Offer_test.cpp:81
ripple::test::Offer_test::testFillModes
void testFillModes(FeatureBitset features)
Definition: Offer_test.cpp:861
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::test::jtx::owner_count
Definition: owners.h:49
ripple::test::jtx::rate
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition: rate.cpp:30
ripple::test::Offer_test::testBridgedSecondLegDry
void testBridgedSecondLegDry(FeatureBitset features)
Definition: Offer_test.cpp:1964