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  jrr = ledgerEntryState(env, bob, gw, "USD");
2130  if (NumberSwitchOver)
2131  {
2132  BEAST_EXPECT(
2133  jrr[jss::node][sfBalance.fieldName][jss::value] ==
2134  "-0.9665000000333333");
2135  }
2136  else
2137  {
2138  BEAST_EXPECT(
2139  jrr[jss::node][sfBalance.fieldName][jss::value] ==
2140  "-0.966500000033334");
2141  }
2142  }
2143  }
2144 
2145  void
2147  {
2148  testcase("Offer tfSell: Basic Sell");
2149 
2150  using namespace jtx;
2151 
2152  Env env{*this, features};
2153 
2154  auto const gw = Account{"gateway"};
2155  auto const alice = Account{"alice"};
2156  auto const bob = Account{"bob"};
2157  auto const USD = gw["USD"];
2158 
2159  auto const starting_xrp = XRP(100) +
2160  env.current()->fees().accountReserve(1) +
2161  env.current()->fees().base * 2;
2162 
2163  env.fund(starting_xrp, gw, alice, bob);
2164 
2165  env(trust(alice, USD(1000)));
2166  env(trust(bob, USD(1000)));
2167 
2168  env(pay(gw, bob, bob["USD"](500)));
2169 
2170  env(offer(bob, XRP(200), USD(200)), json(jss::Flags, tfSell));
2171  // Alice has 350 + fees - a reserve of 50 = 250 reserve = 100 available.
2172  // Alice has 350 + fees - a reserve of 50 = 250 reserve = 100 available.
2173  // Ask for more than available to prove reserve works.
2174  env(offer(alice, USD(200), XRP(200)), json(jss::Flags, tfSell));
2175 
2176  auto jrr = ledgerEntryState(env, alice, gw, "USD");
2177  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
2178  jrr = ledgerEntryRoot(env, alice);
2179  BEAST_EXPECT(
2180  jrr[jss::node][sfBalance.fieldName] ==
2181  STAmount(env.current()->fees().accountReserve(1)).getText());
2182 
2183  jrr = ledgerEntryState(env, bob, gw, "USD");
2184  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400");
2185  }
2186 
2187  void
2189  {
2190  testcase("Offer tfSell: 2x Sell Exceed Limit");
2191 
2192  using namespace jtx;
2193 
2194  Env env{*this, features};
2195 
2196  auto const gw = Account{"gateway"};
2197  auto const alice = Account{"alice"};
2198  auto const bob = Account{"bob"};
2199  auto const USD = gw["USD"];
2200 
2201  auto const starting_xrp = XRP(100) +
2202  env.current()->fees().accountReserve(1) +
2203  env.current()->fees().base * 2;
2204 
2205  env.fund(starting_xrp, gw, alice, bob);
2206 
2207  env(trust(alice, USD(150)));
2208  env(trust(bob, USD(1000)));
2209 
2210  env(pay(gw, bob, bob["USD"](500)));
2211 
2212  env(offer(bob, XRP(100), USD(200)));
2213  // Alice has 350 fees - a reserve of 50 = 250 reserve = 100 available.
2214  // Ask for more than available to prove reserve works.
2215  // Taker pays 100 USD for 100 XRP.
2216  // Selling XRP.
2217  // Will sell all 100 XRP and get more USD than asked for.
2218  env(offer(alice, USD(100), XRP(100)), json(jss::Flags, tfSell));
2219 
2220  auto jrr = ledgerEntryState(env, alice, gw, "USD");
2221  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-200");
2222  jrr = ledgerEntryRoot(env, alice);
2223  BEAST_EXPECT(
2224  jrr[jss::node][sfBalance.fieldName] ==
2225  STAmount(env.current()->fees().accountReserve(1)).getText());
2226 
2227  jrr = ledgerEntryState(env, bob, gw, "USD");
2228  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-300");
2229  }
2230 
2231  void
2233  {
2234  testcase("Client Issue #535: Gateway Cross Currency");
2235 
2236  using namespace jtx;
2237 
2238  Env env{*this, features};
2239 
2240  auto const gw = Account{"gateway"};
2241  auto const alice = Account{"alice"};
2242  auto const bob = Account{"bob"};
2243  auto const XTS = gw["XTS"];
2244  auto const XXX = gw["XXX"];
2245 
2246  auto const starting_xrp = XRP(100.1) +
2247  env.current()->fees().accountReserve(1) +
2248  env.current()->fees().base * 2;
2249 
2250  env.fund(starting_xrp, gw, alice, bob);
2251 
2252  env(trust(alice, XTS(1000)));
2253  env(trust(alice, XXX(1000)));
2254  env(trust(bob, XTS(1000)));
2255  env(trust(bob, XXX(1000)));
2256 
2257  env(pay(gw, alice, alice["XTS"](100)));
2258  env(pay(gw, alice, alice["XXX"](100)));
2259  env(pay(gw, bob, bob["XTS"](100)));
2260  env(pay(gw, bob, bob["XXX"](100)));
2261 
2262  env(offer(alice, XTS(100), XXX(100)));
2263 
2264  // WS client is used here because the RPC client could not
2265  // be convinced to pass the build_path argument
2266  auto wsc = makeWSClient(env.app().config());
2267  Json::Value payment;
2268  payment[jss::secret] = toBase58(generateSeed("bob"));
2269  payment[jss::id] = env.seq(bob);
2270  payment[jss::build_path] = true;
2271  payment[jss::tx_json] = pay(bob, bob, bob["XXX"](1));
2272  payment[jss::tx_json][jss::Sequence] =
2273  env.current()
2274  ->read(keylet::account(bob.id()))
2275  ->getFieldU32(sfSequence);
2276  payment[jss::tx_json][jss::Fee] = to_string(env.current()->fees().base);
2277  payment[jss::tx_json][jss::SendMax] =
2278  bob["XTS"](1.5).value().getJson(JsonOptions::none);
2279  auto jrr = wsc->invoke("submit", payment);
2280  BEAST_EXPECT(jrr[jss::status] == "success");
2281  BEAST_EXPECT(jrr[jss::result][jss::engine_result] == "tesSUCCESS");
2282  if (wsc->version() == 2)
2283  {
2284  BEAST_EXPECT(
2285  jrr.isMember(jss::jsonrpc) && jrr[jss::jsonrpc] == "2.0");
2286  BEAST_EXPECT(
2287  jrr.isMember(jss::ripplerpc) && jrr[jss::ripplerpc] == "2.0");
2288  BEAST_EXPECT(jrr.isMember(jss::id) && jrr[jss::id] == 5);
2289  }
2290 
2291  jrr = ledgerEntryState(env, alice, gw, "XTS");
2292  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101");
2293  jrr = ledgerEntryState(env, alice, gw, "XXX");
2294  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-99");
2295 
2296  jrr = ledgerEntryState(env, bob, gw, "XTS");
2297  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-99");
2298  jrr = ledgerEntryState(env, bob, gw, "XXX");
2299  BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101");
2300  }
2301 
2302  // Helper function that validates a *defaulted* trustline: one that has
2303  // no unusual flags set and doesn't have high or low limits set. Such a
2304  // trustline may have an actual balance (it can be created automatically
2305  // if a user places an offer to acquire an IOU for which they don't have
2306  // a trust line defined). If the trustline is not defaulted then the tests
2307  // will not pass.
2308  void
2310  jtx::Env& env,
2311  jtx::Account const& account,
2312  jtx::PrettyAmount const& expectBalance)
2313  {
2314  auto const sleTrust =
2315  env.le(keylet::line(account.id(), expectBalance.value().issue()));
2316  BEAST_EXPECT(sleTrust);
2317  if (sleTrust)
2318  {
2319  Issue const issue = expectBalance.value().issue();
2320  bool const accountLow = account.id() < issue.account;
2321 
2322  STAmount low{issue};
2323  STAmount high{issue};
2324 
2325  low.setIssuer(accountLow ? account.id() : issue.account);
2326  high.setIssuer(accountLow ? issue.account : account.id());
2327 
2328  BEAST_EXPECT(sleTrust->getFieldAmount(sfLowLimit) == low);
2329  BEAST_EXPECT(sleTrust->getFieldAmount(sfHighLimit) == high);
2330 
2331  STAmount actualBalance{sleTrust->getFieldAmount(sfBalance)};
2332  if (!accountLow)
2333  actualBalance.negate();
2334 
2335  BEAST_EXPECT(actualBalance == expectBalance);
2336  }
2337  }
2338 
2339  void
2341  {
2342  // Test a number of different corner cases regarding adding a
2343  // possibly crossable offer to an account. The test is table
2344  // driven so it should be easy to add or remove tests.
2345  testcase("Partial Crossing");
2346 
2347  using namespace jtx;
2348 
2349  auto const gw = Account("gateway");
2350  auto const USD = gw["USD"];
2351 
2352  Env env{*this, features};
2353 
2354  env.fund(XRP(10000000), gw);
2355 
2356  // The fee that's charged for transactions
2357  auto const f = env.current()->fees().base;
2358 
2359  // To keep things simple all offers are 1 : 1 for XRP : USD.
2360  enum preTrustType { noPreTrust, gwPreTrust, acctPreTrust };
2361  struct TestData
2362  {
2363  std::string account; // Account operated on
2364  STAmount fundXrp; // Account funded with
2365  int bookAmount; // USD -> XRP offer on the books
2366  preTrustType preTrust; // If true, pre-establish trust line
2367  int offerAmount; // Account offers this much XRP -> USD
2368  TER tec; // Returned tec code
2369  STAmount spentXrp; // Amount removed from fundXrp
2370  PrettyAmount balanceUsd; // Balance on account end
2371  int offers; // Offers on account
2372  int owners; // Owners on account
2373  };
2374 
2375  // clang-format off
2376  TestData const tests[]{
2377  // acct fundXrp bookAmt preTrust offerAmt tec spentXrp balanceUSD offers owners
2378  {"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.
2379  {"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.
2380  {"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.
2381  {"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.
2382  {"eve", reserve(env, 1) + 0 * f, 0, noPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1}, // No offer to cross
2383  {"flo", reserve(env, 1) + 0 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 0, 1},
2384  {"gay", reserve(env, 1) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 50) + f, USD( 50), 0, 1},
2385  {"hye", XRP(1000) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 800) + f, USD( 800), 0, 1},
2386  {"ivy", XRP( 1) + reserve(env, 1) + 1 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 0, 1},
2387  {"joy", XRP( 1) + reserve(env, 2) + 1 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 2},
2388  {"kim", XRP( 900) + reserve(env, 2) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
2389  {"liz", XRP( 998) + reserve(env, 0) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 998) + f, USD( 998), 0, 1},
2390  {"meg", XRP( 998) + reserve(env, 1) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
2391  {"nia", XRP( 998) + reserve(env, 2) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 1, 2},
2392  {"ova", XRP( 999) + reserve(env, 0) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
2393  {"pam", XRP( 999) + reserve(env, 1) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP(1000) + f, USD( 1000), 0, 1},
2394  {"rae", XRP( 999) + reserve(env, 2) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP(1000) + f, USD( 1000), 0, 1},
2395  {"sue", XRP(1000) + reserve(env, 2) + 1 * f, 0, noPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1},
2396  //---------------- Pre-established trust lines ---------------------
2397  {"abe", reserve(env, 0) + 0 * f, 1, gwPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0},
2398  {"bud", reserve(env, 0) + 1 * f, 1, gwPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0},
2399  {"che", reserve(env, 0) + 2 * f, 0, gwPreTrust, 1000, tecINSUF_RESERVE_OFFER, f, USD( 0), 0, 0},
2400  {"dan", reserve(env, 0) + 2 * f, 1, gwPreTrust, 1000, tesSUCCESS, 2 * f, USD(0.00001), 0, 0},
2401  {"eli", XRP( 20) + reserve(env, 0) + 1 * f, 1000, gwPreTrust, 1000, tesSUCCESS, XRP(20) + 1 * f, USD( 20), 0, 0},
2402  {"fyn", reserve(env, 1) + 0 * f, 0, gwPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1},
2403  {"gar", reserve(env, 1) + 0 * f, 1, gwPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 1},
2404  {"hal", reserve(env, 1) + 1 * f, 1, gwPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 1},
2405 
2406  {"ned", reserve(env, 1) + 0 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
2407  {"ole", reserve(env, 1) + 1 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
2408  {"pat", reserve(env, 1) + 2 * f, 0, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
2409  {"quy", reserve(env, 1) + 2 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
2410  {"ron", reserve(env, 1) + 3 * f, 0, acctPreTrust, 1000, tecINSUF_RESERVE_OFFER, 2 * f, USD( 0), 0, 1},
2411  {"syd", reserve(env, 1) + 3 * f, 1, acctPreTrust, 1000, tesSUCCESS, 3 * f, USD(0.00001), 0, 1},
2412  {"ted", XRP( 20) + reserve(env, 1) + 2 * f, 1000, acctPreTrust, 1000, tesSUCCESS, XRP(20) + 2 * f, USD( 20), 0, 1},
2413  {"uli", reserve(env, 2) + 0 * f, 0, acctPreTrust, 1000, tecINSUF_RESERVE_OFFER, 2 * f, USD( 0), 0, 1},
2414  {"vic", reserve(env, 2) + 0 * f, 1, acctPreTrust, 1000, tesSUCCESS, XRP( 1) + 2 * f, USD( 1), 0, 1},
2415  {"wes", reserve(env, 2) + 1 * f, 0, acctPreTrust, 1000, tesSUCCESS, 2 * f, USD( 0), 1, 2},
2416  {"xan", reserve(env, 2) + 1 * f, 1, acctPreTrust, 1000, tesSUCCESS, XRP( 1) + 2 * f, USD( 1), 1, 2},
2417  };
2418  // clang-format on
2419 
2420  for (auto const& t : tests)
2421  {
2422  auto const acct = Account(t.account);
2423  env.fund(t.fundXrp, acct);
2424  env.close();
2425 
2426  // Make sure gateway has no current offers.
2427  env.require(offers(gw, 0));
2428 
2429  // The gateway optionally creates an offer that would be crossed.
2430  auto const book = t.bookAmount;
2431  if (book)
2432  env(offer(gw, XRP(book), USD(book)));
2433  env.close();
2434  std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
2435 
2436  // Optionally pre-establish a trustline between gw and acct.
2437  if (t.preTrust == gwPreTrust)
2438  env(trust(gw, acct["USD"](1)));
2439 
2440  // Optionally pre-establish a trustline between acct and gw.
2441  // Note this is not really part of the test, so we expect there
2442  // to be enough XRP reserve for acct to create the trust line.
2443  if (t.preTrust == acctPreTrust)
2444  env(trust(acct, USD(1)));
2445 
2446  env.close();
2447 
2448  {
2449  // Acct creates an offer. This is the heart of the test.
2450  auto const acctOffer = t.offerAmount;
2451  env(offer(acct, USD(acctOffer), XRP(acctOffer)), ter(t.tec));
2452  env.close();
2453  }
2454  std::uint32_t const acctOfferSeq = env.seq(acct) - 1;
2455 
2456  BEAST_EXPECT(env.balance(acct, USD.issue()) == t.balanceUsd);
2457  BEAST_EXPECT(
2458  env.balance(acct, xrpIssue()) == t.fundXrp - t.spentXrp);
2459  env.require(offers(acct, t.offers));
2460  env.require(owners(acct, t.owners));
2461 
2462  auto acctOffers = offersOnAccount(env, acct);
2463  BEAST_EXPECT(acctOffers.size() == t.offers);
2464  if (acctOffers.size() && t.offers)
2465  {
2466  auto const& acctOffer = *(acctOffers.front());
2467 
2468  auto const leftover = t.offerAmount - t.bookAmount;
2469  BEAST_EXPECT(acctOffer[sfTakerGets] == XRP(leftover));
2470  BEAST_EXPECT(acctOffer[sfTakerPays] == USD(leftover));
2471  }
2472 
2473  if (t.preTrust == noPreTrust)
2474  {
2475  if (t.balanceUsd.value().signum())
2476  {
2477  // Verify the correct contents of the trustline
2478  verifyDefaultTrustline(env, acct, t.balanceUsd);
2479  }
2480  else
2481  {
2482  // Verify that no trustline was created.
2483  auto const sleTrust =
2484  env.le(keylet::line(acct, USD.issue()));
2485  BEAST_EXPECT(!sleTrust);
2486  }
2487  }
2488 
2489  // Give the next loop a clean slate by canceling any left-overs
2490  // in the offers.
2491  env(offer_cancel(acct, acctOfferSeq));
2492  env(offer_cancel(gw, gwOfferSeq));
2493  env.close();
2494  }
2495  }
2496 
2497  void
2499  {
2500  testcase("XRP Direct Crossing");
2501 
2502  using namespace jtx;
2503 
2504  auto const gw = Account("gateway");
2505  auto const alice = Account("alice");
2506  auto const bob = Account("bob");
2507  auto const USD = gw["USD"];
2508 
2509  auto const usdOffer = USD(1000);
2510  auto const xrpOffer = XRP(1000);
2511 
2512  Env env{*this, features};
2513 
2514  env.fund(XRP(1000000), gw, bob);
2515  env.close();
2516 
2517  // The fee that's charged for transactions.
2518  auto const fee = env.current()->fees().base;
2519 
2520  // alice's account has enough for the reserve, one trust line plus two
2521  // offers, and two fees.
2522  env.fund(reserve(env, 2) + fee * 2, alice);
2523  env.close();
2524 
2525  env(trust(alice, usdOffer));
2526 
2527  env.close();
2528 
2529  env(pay(gw, alice, usdOffer));
2530  env.close();
2531  env.require(balance(alice, usdOffer), offers(alice, 0), offers(bob, 0));
2532 
2533  // The scenario:
2534  // o alice has USD but wants XRP.
2535  // o bob has XRP but wants USD.
2536  auto const alicesXRP = env.balance(alice);
2537  auto const bobsXRP = env.balance(bob);
2538 
2539  env(offer(alice, xrpOffer, usdOffer));
2540  env.close();
2541  env(offer(bob, usdOffer, xrpOffer));
2542 
2543  env.close();
2544  env.require(
2545  balance(alice, USD(0)),
2546  balance(bob, usdOffer),
2547  balance(alice, alicesXRP + xrpOffer - fee),
2548  balance(bob, bobsXRP - xrpOffer - fee),
2549  offers(alice, 0),
2550  offers(bob, 0));
2551 
2552  verifyDefaultTrustline(env, bob, usdOffer);
2553 
2554  // Make two more offers that leave one of the offers non-dry.
2555  env(offer(alice, USD(999), XRP(999)));
2556  env(offer(bob, xrpOffer, usdOffer));
2557 
2558  env.close();
2559  env.require(balance(alice, USD(999)));
2560  env.require(balance(bob, USD(1)));
2561  env.require(offers(alice, 0));
2562  verifyDefaultTrustline(env, bob, USD(1));
2563  {
2564  auto const bobsOffers = offersOnAccount(env, bob);
2565  BEAST_EXPECT(bobsOffers.size() == 1);
2566  auto const& bobsOffer = *(bobsOffers.front());
2567 
2568  BEAST_EXPECT(bobsOffer[sfLedgerEntryType] == ltOFFER);
2569  BEAST_EXPECT(bobsOffer[sfTakerGets] == USD(1));
2570  BEAST_EXPECT(bobsOffer[sfTakerPays] == XRP(1));
2571  }
2572  }
2573 
2574  void
2576  {
2577  testcase("Direct Crossing");
2578 
2579  using namespace jtx;
2580 
2581  auto const gw = Account("gateway");
2582  auto const alice = Account("alice");
2583  auto const bob = Account("bob");
2584  auto const USD = gw["USD"];
2585  auto const EUR = gw["EUR"];
2586 
2587  auto const usdOffer = USD(1000);
2588  auto const eurOffer = EUR(1000);
2589 
2590  Env env{*this, features};
2591 
2592  env.fund(XRP(1000000), gw);
2593  env.close();
2594 
2595  // The fee that's charged for transactions.
2596  auto const fee = env.current()->fees().base;
2597 
2598  // Each account has enough for the reserve, two trust lines, one
2599  // offer, and two fees.
2600  env.fund(reserve(env, 3) + fee * 3, alice);
2601  env.fund(reserve(env, 3) + fee * 2, bob);
2602  env.close();
2603 
2604  env(trust(alice, usdOffer));
2605  env(trust(bob, eurOffer));
2606  env.close();
2607 
2608  env(pay(gw, alice, usdOffer));
2609  env(pay(gw, bob, eurOffer));
2610  env.close();
2611 
2612  env.require(balance(alice, usdOffer), balance(bob, eurOffer));
2613 
2614  // The scenario:
2615  // o alice has USD but wants EUR.
2616  // o bob has EUR but wants USD.
2617  env(offer(alice, eurOffer, usdOffer));
2618  env(offer(bob, usdOffer, eurOffer));
2619 
2620  env.close();
2621  env.require(
2622  balance(alice, eurOffer),
2623  balance(bob, usdOffer),
2624  offers(alice, 0),
2625  offers(bob, 0));
2626 
2627  // Alice's offer crossing created a default EUR trustline and
2628  // Bob's offer crossing created a default USD trustline:
2629  verifyDefaultTrustline(env, alice, eurOffer);
2630  verifyDefaultTrustline(env, bob, usdOffer);
2631 
2632  // Make two more offers that leave one of the offers non-dry.
2633  // Guarantee the order of application by putting a close()
2634  // between them.
2635  env(offer(bob, eurOffer, usdOffer));
2636  env.close();
2637 
2638  env(offer(alice, USD(999), eurOffer));
2639  env.close();
2640 
2641  env.require(offers(alice, 0));
2642  env.require(offers(bob, 1));
2643 
2644  env.require(balance(alice, USD(999)));
2645  env.require(balance(alice, EUR(1)));
2646  env.require(balance(bob, USD(1)));
2647  env.require(balance(bob, EUR(999)));
2648 
2649  {
2650  auto bobsOffers = offersOnAccount(env, bob);
2651  if (BEAST_EXPECT(bobsOffers.size() == 1))
2652  {
2653  auto const& bobsOffer = *(bobsOffers.front());
2654 
2655  BEAST_EXPECT(bobsOffer[sfTakerGets] == USD(1));
2656  BEAST_EXPECT(bobsOffer[sfTakerPays] == EUR(1));
2657  }
2658  }
2659 
2660  // alice makes one more offer that cleans out bob's offer.
2661  env(offer(alice, USD(1), EUR(1)));
2662  env.close();
2663 
2664  env.require(balance(alice, USD(1000)));
2665  env.require(balance(alice, EUR(none)));
2666  env.require(balance(bob, USD(none)));
2667  env.require(balance(bob, EUR(1000)));
2668  env.require(offers(alice, 0));
2669  env.require(offers(bob, 0));
2670 
2671  // The two trustlines that were generated by offers should be gone.
2672  BEAST_EXPECT(!env.le(keylet::line(alice.id(), EUR.issue())));
2673  BEAST_EXPECT(!env.le(keylet::line(bob.id(), USD.issue())));
2674 
2675  // Make two more offers that leave one of the offers non-dry. We
2676  // need to properly sequence the transactions:
2677  env(offer(alice, EUR(999), usdOffer));
2678  env.close();
2679 
2680  env(offer(bob, usdOffer, eurOffer));
2681  env.close();
2682 
2683  env.require(offers(alice, 0));
2684  env.require(offers(bob, 0));
2685 
2686  env.require(balance(alice, USD(0)));
2687  env.require(balance(alice, EUR(999)));
2688  env.require(balance(bob, USD(1000)));
2689  env.require(balance(bob, EUR(1)));
2690  }
2691 
2692  void
2694  {
2695  testcase("Bridged Crossing");
2696 
2697  using namespace jtx;
2698 
2699  auto const gw = Account("gateway");
2700  auto const alice = Account("alice");
2701  auto const bob = Account("bob");
2702  auto const carol = Account("carol");
2703  auto const USD = gw["USD"];
2704  auto const EUR = gw["EUR"];
2705 
2706  auto const usdOffer = USD(1000);
2707  auto const eurOffer = EUR(1000);
2708 
2709  Env env{*this, features};
2710 
2711  env.fund(XRP(1000000), gw, alice, bob, carol);
2712  env.close();
2713 
2714  env(trust(alice, usdOffer));
2715  env(trust(carol, eurOffer));
2716  env.close();
2717  env(pay(gw, alice, usdOffer));
2718  env(pay(gw, carol, eurOffer));
2719  env.close();
2720 
2721  // The scenario:
2722  // o alice has USD but wants XPR.
2723  // o bob has XRP but wants EUR.
2724  // o carol has EUR but wants USD.
2725  // Note that carol's offer must come last. If carol's offer is placed
2726  // before bob's or alice's, then autobridging will not occur.
2727  env(offer(alice, XRP(1000), usdOffer));
2728  env(offer(bob, eurOffer, XRP(1000)));
2729  auto const bobXrpBalance = env.balance(bob);
2730  env.close();
2731 
2732  // carol makes an offer that partially consumes alice and bob's offers.
2733  env(offer(carol, USD(400), EUR(400)));
2734  env.close();
2735 
2736  env.require(
2737  balance(alice, USD(600)),
2738  balance(bob, EUR(400)),
2739  balance(carol, USD(400)),
2740  balance(bob, bobXrpBalance - XRP(400)),
2741  offers(carol, 0));
2742  verifyDefaultTrustline(env, bob, EUR(400));
2743  verifyDefaultTrustline(env, carol, USD(400));
2744  {
2745  auto const alicesOffers = offersOnAccount(env, alice);
2746  BEAST_EXPECT(alicesOffers.size() == 1);
2747  auto const& alicesOffer = *(alicesOffers.front());
2748 
2749  BEAST_EXPECT(alicesOffer[sfLedgerEntryType] == ltOFFER);
2750  BEAST_EXPECT(alicesOffer[sfTakerGets] == USD(600));
2751  BEAST_EXPECT(alicesOffer[sfTakerPays] == XRP(600));
2752  }
2753  {
2754  auto const bobsOffers = offersOnAccount(env, bob);
2755  BEAST_EXPECT(bobsOffers.size() == 1);
2756  auto const& bobsOffer = *(bobsOffers.front());
2757 
2758  BEAST_EXPECT(bobsOffer[sfLedgerEntryType] == ltOFFER);
2759  BEAST_EXPECT(bobsOffer[sfTakerGets] == XRP(600));
2760  BEAST_EXPECT(bobsOffer[sfTakerPays] == EUR(600));
2761  }
2762 
2763  // carol makes an offer that exactly consumes alice and bob's offers.
2764  env(offer(carol, USD(600), EUR(600)));
2765  env.close();
2766 
2767  env.require(
2768  balance(alice, USD(0)),
2769  balance(bob, eurOffer),
2770  balance(carol, usdOffer),
2771  balance(bob, bobXrpBalance - XRP(1000)),
2772  offers(bob, 0),
2773  offers(carol, 0));
2774  verifyDefaultTrustline(env, bob, EUR(1000));
2775  verifyDefaultTrustline(env, carol, USD(1000));
2776 
2777  // In pre-flow code alice's offer is left empty in the ledger.
2778  auto const alicesOffers = offersOnAccount(env, alice);
2779  if (alicesOffers.size() != 0)
2780  {
2781  BEAST_EXPECT(alicesOffers.size() == 1);
2782  auto const& alicesOffer = *(alicesOffers.front());
2783 
2784  BEAST_EXPECT(alicesOffer[sfLedgerEntryType] == ltOFFER);
2785  BEAST_EXPECT(alicesOffer[sfTakerGets] == USD(0));
2786  BEAST_EXPECT(alicesOffer[sfTakerPays] == XRP(0));
2787  }
2788  }
2789 
2790  void
2792  {
2793  // Test a number of different corner cases regarding offer crossing
2794  // when the tfSell flag is set. The test is table driven so it
2795  // should be easy to add or remove tests.
2796  testcase("Sell Offer");
2797 
2798  using namespace jtx;
2799 
2800  auto const gw = Account("gateway");
2801  auto const USD = gw["USD"];
2802 
2803  Env env{*this, features};
2804 
2805  env.fund(XRP(10000000), gw);
2806 
2807  // The fee that's charged for transactions
2808  auto const f = env.current()->fees().base;
2809 
2810  // To keep things simple all offers are 1 : 1 for XRP : USD.
2811  enum preTrustType { noPreTrust, gwPreTrust, acctPreTrust };
2812  struct TestData
2813  {
2814  std::string account; // Account operated on
2815  STAmount fundXrp; // XRP acct funded with
2816  STAmount fundUSD; // USD acct funded with
2817  STAmount gwGets; // gw's offer
2818  STAmount gwPays; //
2819  STAmount acctGets; // acct's offer
2820  STAmount acctPays; //
2821  TER tec; // Returned tec code
2822  STAmount spentXrp; // Amount removed from fundXrp
2823  STAmount finalUsd; // Final USD balance on acct
2824  int offers; // Offers on acct
2825  int owners; // Owners on acct
2826  STAmount takerGets; // Remainder of acct's offer
2827  STAmount takerPays; //
2828 
2829  // Constructor with takerGets/takerPays
2830  TestData(
2831  std::string&& account_, // Account operated on
2832  STAmount const& fundXrp_, // XRP acct funded with
2833  STAmount const& fundUSD_, // USD acct funded with
2834  STAmount const& gwGets_, // gw's offer
2835  STAmount const& gwPays_, //
2836  STAmount const& acctGets_, // acct's offer
2837  STAmount const& acctPays_, //
2838  TER tec_, // Returned tec code
2839  STAmount const& spentXrp_, // Amount removed from fundXrp
2840  STAmount const& finalUsd_, // Final USD balance on acct
2841  int offers_, // Offers on acct
2842  int owners_, // Owners on acct
2843  STAmount const& takerGets_, // Remainder of acct's offer
2844  STAmount const& takerPays_) //
2845  : account(std::move(account_))
2846  , fundXrp(fundXrp_)
2847  , fundUSD(fundUSD_)
2848  , gwGets(gwGets_)
2849  , gwPays(gwPays_)
2850  , acctGets(acctGets_)
2851  , acctPays(acctPays_)
2852  , tec(tec_)
2853  , spentXrp(spentXrp_)
2854  , finalUsd(finalUsd_)
2855  , offers(offers_)
2856  , owners(owners_)
2857  , takerGets(takerGets_)
2858  , takerPays(takerPays_)
2859  {
2860  }
2861 
2862  // Constructor without takerGets/takerPays
2863  TestData(
2864  std::string&& account_, // Account operated on
2865  STAmount const& fundXrp_, // XRP acct funded with
2866  STAmount const& fundUSD_, // USD acct funded with
2867  STAmount const& gwGets_, // gw's offer
2868  STAmount const& gwPays_, //
2869  STAmount const& acctGets_, // acct's offer
2870  STAmount const& acctPays_, //
2871  TER tec_, // Returned tec code
2872  STAmount const& spentXrp_, // Amount removed from fundXrp
2873  STAmount const& finalUsd_, // Final USD balance on acct
2874  int offers_, // Offers on acct
2875  int owners_) // Owners on acct
2876  : TestData(
2877  std::move(account_),
2878  fundXrp_,
2879  fundUSD_,
2880  gwGets_,
2881  gwPays_,
2882  acctGets_,
2883  acctPays_,
2884  tec_,
2885  spentXrp_,
2886  finalUsd_,
2887  offers_,
2888  owners_,
2889  STAmount{0},
2890  STAmount{0})
2891  {
2892  }
2893  };
2894 
2895  // clang-format off
2896  TestData const tests[]{
2897  // acct pays XRP
2898  // acct fundXrp fundUSD gwGets gwPays acctGets acctPays tec spentXrp finalUSD offers owners takerGets takerPays
2899  {"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},
2900  {"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)},
2901  {"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},
2902  {"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},
2903  {"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},
2904  {"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},
2905  {"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},
2906  {"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)},
2907  // acct pays USD
2908  {"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},
2909  {"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)},
2910  {"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},
2911  {"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},
2912  {"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},
2913  {"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)},
2914  {"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)},
2915  };
2916  // clang-format on
2917 
2918  auto const zeroUsd = USD(0);
2919  for (auto const& t : tests)
2920  {
2921  // Make sure gateway has no current offers.
2922  env.require(offers(gw, 0));
2923 
2924  auto const acct = Account(t.account);
2925 
2926  env.fund(t.fundXrp, acct);
2927  env.close();
2928 
2929  // Optionally give acct some USD. This is not part of the test,
2930  // so we assume that acct has sufficient USD to cover the reserve
2931  // on the trust line.
2932  if (t.fundUSD != zeroUsd)
2933  {
2934  env(trust(acct, t.fundUSD));
2935  env.close();
2936  env(pay(gw, acct, t.fundUSD));
2937  env.close();
2938  }
2939 
2940  env(offer(gw, t.gwGets, t.gwPays));
2941  env.close();
2942  std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
2943 
2944  // Acct creates a tfSell offer. This is the heart of the test.
2945  env(offer(acct, t.acctGets, t.acctPays, tfSell), ter(t.tec));
2946  env.close();
2947  std::uint32_t const acctOfferSeq = env.seq(acct) - 1;
2948 
2949  // Check results
2950  BEAST_EXPECT(env.balance(acct, USD.issue()) == t.finalUsd);
2951  BEAST_EXPECT(
2952  env.balance(acct, xrpIssue()) == t.fundXrp - t.spentXrp);
2953  env.require(offers(acct, t.offers));
2954  env.require(owners(acct, t.owners));
2955 
2956  if (t.offers)
2957  {
2958  auto const acctOffers = offersOnAccount(env, acct);
2959  if (acctOffers.size() > 0)
2960  {
2961  BEAST_EXPECT(acctOffers.size() == 1);
2962  auto const& acctOffer = *(acctOffers.front());
2963 
2964  BEAST_EXPECT(acctOffer[sfLedgerEntryType] == ltOFFER);
2965  BEAST_EXPECT(acctOffer[sfTakerGets] == t.takerGets);
2966  BEAST_EXPECT(acctOffer[sfTakerPays] == t.takerPays);
2967  }
2968  }
2969 
2970  // Give the next loop a clean slate by canceling any left-overs
2971  // in the offers.
2972  env(offer_cancel(acct, acctOfferSeq));
2973  env(offer_cancel(gw, gwOfferSeq));
2974  env.close();
2975  }
2976  }
2977 
2978  void
2980  {
2981  // Test a number of different corner cases regarding offer crossing
2982  // when both the tfSell flag and tfFillOrKill flags are set.
2983  testcase("Combine tfSell with tfFillOrKill");
2984 
2985  using namespace jtx;
2986 
2987  auto const gw = Account("gateway");
2988  auto const alice = Account("alice");
2989  auto const bob = Account("bob");
2990  auto const USD = gw["USD"];
2991 
2992  Env env{*this, features};
2993 
2994  env.fund(XRP(10000000), gw, alice, bob);
2995 
2996  // Code returned if an offer is killed.
2997  TER const killedCode{
2998  features[fix1578] ? TER{tecKILLED} : TER{tesSUCCESS}};
2999 
3000  // bob offers XRP for USD.
3001  env(trust(bob, USD(200)));
3002  env.close();
3003  env(pay(gw, bob, USD(100)));
3004  env.close();
3005  env(offer(bob, XRP(2000), USD(20)));
3006  env.close();
3007  {
3008  // alice submits a tfSell | tfFillOrKill offer that does not cross.
3009  env(offer(alice, USD(21), XRP(2100), tfSell | tfFillOrKill),
3010  ter(killedCode));
3011  env.close();
3012  env.require(balance(alice, USD(none)));
3013  env.require(offers(alice, 0));
3014  env.require(balance(bob, USD(100)));
3015  }
3016  {
3017  // alice submits a tfSell | tfFillOrKill offer that crosses.
3018  // Even though tfSell is present it doesn't matter this time.
3019  env(offer(alice, USD(20), XRP(2000), tfSell | tfFillOrKill));
3020  env.close();
3021  env.require(balance(alice, USD(20)));
3022  env.require(offers(alice, 0));
3023  env.require(balance(bob, USD(80)));
3024  }
3025  {
3026  // alice submits a tfSell | tfFillOrKill offer that crosses and
3027  // returns more than was asked for (because of the tfSell flag).
3028  env(offer(bob, XRP(2000), USD(20)));
3029  env.close();
3030  env(offer(alice, USD(10), XRP(1500), tfSell | tfFillOrKill));
3031  env.close();
3032  env.require(balance(alice, USD(35)));
3033  env.require(offers(alice, 0));
3034  env.require(balance(bob, USD(65)));
3035  }
3036  {
3037  // alice submits a tfSell | tfFillOrKill offer that doesn't cross.
3038  // This would have succeeded with a regular tfSell, but the
3039  // fillOrKill prevents the transaction from crossing since not
3040  // all of the offer is consumed.
3041 
3042  // We're using bob's left-over offer for XRP(500), USD(5)
3043  env(offer(alice, USD(1), XRP(501), tfSell | tfFillOrKill),
3044  ter(killedCode));
3045  env.close();
3046  env.require(balance(alice, USD(35)));
3047  env.require(offers(alice, 0));
3048  env.require(balance(bob, USD(65)));
3049  }
3050  {
3051  // Alice submits a tfSell | tfFillOrKill offer that finishes
3052  // off the remainder of bob's offer.
3053 
3054  // We're using bob's left-over offer for XRP(500), USD(5)
3055  env(offer(alice, USD(1), XRP(500), tfSell | tfFillOrKill));
3056  env.close();
3057  env.require(balance(alice, USD(40)));
3058  env.require(offers(alice, 0));
3059  env.require(balance(bob, USD(60)));
3060  }
3061  }
3062 
3063  void
3065  {
3066  testcase("Transfer Rate Offer");
3067 
3068  using namespace jtx;
3069 
3070  auto const gw1 = Account("gateway1");
3071  auto const USD = gw1["USD"];
3072 
3073  Env env{*this, features};
3074 
3075  // The fee that's charged for transactions.
3076  auto const fee = env.current()->fees().base;
3077 
3078  env.fund(XRP(100000), gw1);
3079  env.close();
3080 
3081  env(rate(gw1, 1.25));
3082  {
3083  auto const ann = Account("ann");
3084  auto const bob = Account("bob");
3085  env.fund(XRP(100) + reserve(env, 2) + (fee * 2), ann, bob);
3086  env.close();
3087 
3088  env(trust(ann, USD(200)));
3089  env(trust(bob, USD(200)));
3090  env.close();
3091 
3092  env(pay(gw1, bob, USD(125)));
3093  env.close();
3094 
3095  // bob offers to sell USD(100) for XRP. alice takes bob's offer.
3096  // Notice that although bob only offered USD(100), USD(125) was
3097  // removed from his account due to the gateway fee.
3098  //
3099  // A comparable payment would look like this:
3100  // env (pay (bob, alice, USD(100)), sendmax(USD(125)))
3101  env(offer(bob, XRP(1), USD(100)));
3102  env.close();
3103 
3104  env(offer(ann, USD(100), XRP(1)));
3105  env.close();
3106 
3107  env.require(balance(ann, USD(100)));
3108  env.require(balance(ann, XRP(99) + reserve(env, 2)));
3109  env.require(offers(ann, 0));
3110 
3111  env.require(balance(bob, USD(0)));
3112  env.require(balance(bob, XRP(101) + reserve(env, 2)));
3113  env.require(offers(bob, 0));
3114  }
3115  {
3116  // Reverse the order, so the offer in the books is to sell XRP
3117  // in return for USD. Gateway rate should still apply identically.
3118  auto const che = Account("che");
3119  auto const deb = Account("deb");
3120  env.fund(XRP(100) + reserve(env, 2) + (fee * 2), che, deb);
3121  env.close();
3122 
3123  env(trust(che, USD(200)));
3124  env(trust(deb, USD(200)));
3125  env.close();
3126 
3127  env(pay(gw1, deb, USD(125)));
3128  env.close();
3129 
3130  env(offer(che, USD(100), XRP(1)));
3131  env.close();
3132 
3133  env(offer(deb, XRP(1), USD(100)));
3134  env.close();
3135 
3136  env.require(balance(che, USD(100)));
3137  env.require(balance(che, XRP(99) + reserve(env, 2)));
3138  env.require(offers(che, 0));
3139 
3140  env.require(balance(deb, USD(0)));
3141  env.require(balance(deb, XRP(101) + reserve(env, 2)));
3142  env.require(offers(deb, 0));
3143  }
3144  {
3145  auto const eve = Account("eve");
3146  auto const fyn = Account("fyn");
3147 
3148  env.fund(XRP(20000) + (fee * 2), eve, fyn);
3149  env.close();
3150 
3151  env(trust(eve, USD(1000)));
3152  env(trust(fyn, USD(1000)));
3153  env.close();
3154 
3155  env(pay(gw1, eve, USD(100)));
3156  env(pay(gw1, fyn, USD(100)));
3157  env.close();
3158 
3159  // This test verifies that the amount removed from an offer
3160  // accounts for the transfer fee that is removed from the
3161  // account but not from the remaining offer.
3162  env(offer(eve, USD(10), XRP(4000)));
3163  env.close();
3164  std::uint32_t const eveOfferSeq = env.seq(eve) - 1;
3165 
3166  env(offer(fyn, XRP(2000), USD(5)));
3167  env.close();
3168 
3169  env.require(balance(eve, USD(105)));
3170  env.require(balance(eve, XRP(18000)));
3171  auto const evesOffers = offersOnAccount(env, eve);
3172  BEAST_EXPECT(evesOffers.size() == 1);
3173  if (evesOffers.size() != 0)
3174  {
3175  auto const& evesOffer = *(evesOffers.front());
3176  BEAST_EXPECT(evesOffer[sfLedgerEntryType] == ltOFFER);
3177  BEAST_EXPECT(evesOffer[sfTakerGets] == XRP(2000));
3178  BEAST_EXPECT(evesOffer[sfTakerPays] == USD(5));
3179  }
3180  env(offer_cancel(eve, eveOfferSeq)); // For later tests
3181 
3182  env.require(balance(fyn, USD(93.75)));
3183  env.require(balance(fyn, XRP(22000)));
3184  env.require(offers(fyn, 0));
3185  }
3186  // Start messing with two non-native currencies.
3187  auto const gw2 = Account("gateway2");
3188  auto const EUR = gw2["EUR"];
3189 
3190  env.fund(XRP(100000), gw2);
3191  env.close();
3192 
3193  env(rate(gw2, 1.5));
3194  {
3195  // Remove XRP from the equation. Give the two currencies two
3196  // different transfer rates so we can see both transfer rates
3197  // apply in the same transaction.
3198  auto const gay = Account("gay");
3199  auto const hal = Account("hal");
3200  env.fund(reserve(env, 3) + (fee * 3), gay, hal);
3201  env.close();
3202 
3203  env(trust(gay, USD(200)));
3204  env(trust(gay, EUR(200)));
3205  env(trust(hal, USD(200)));
3206  env(trust(hal, EUR(200)));
3207  env.close();
3208 
3209  env(pay(gw1, gay, USD(125)));
3210  env(pay(gw2, hal, EUR(150)));
3211  env.close();
3212 
3213  env(offer(gay, EUR(100), USD(100)));
3214  env.close();
3215 
3216  env(offer(hal, USD(100), EUR(100)));
3217  env.close();
3218 
3219  env.require(balance(gay, USD(0)));
3220  env.require(balance(gay, EUR(100)));
3221  env.require(balance(gay, reserve(env, 3)));
3222  env.require(offers(gay, 0));
3223 
3224  env.require(balance(hal, USD(100)));
3225  env.require(balance(hal, EUR(0)));
3226  env.require(balance(hal, reserve(env, 3)));
3227  env.require(offers(hal, 0));
3228  }
3229  {
3230  // A trust line's QualityIn should not affect offer crossing.
3231  auto const ivy = Account("ivy");
3232  auto const joe = Account("joe");
3233  env.fund(reserve(env, 3) + (fee * 3), ivy, joe);
3234  env.close();
3235 
3236  env(trust(ivy, USD(400)), qualityInPercent(90));
3237  env(trust(ivy, EUR(400)), qualityInPercent(80));
3238  env(trust(joe, USD(400)), qualityInPercent(70));
3239  env(trust(joe, EUR(400)), qualityInPercent(60));
3240  env.close();
3241 
3242  env(pay(gw1, ivy, USD(270)), sendmax(USD(500)));
3243  env(pay(gw2, joe, EUR(150)), sendmax(EUR(300)));
3244  env.close();
3245  env.require(balance(ivy, USD(300)));
3246  env.require(balance(joe, EUR(250)));
3247 
3248  env(offer(ivy, EUR(100), USD(200)));
3249  env.close();
3250 
3251  env(offer(joe, USD(200), EUR(100)));
3252  env.close();
3253 
3254  env.require(balance(ivy, USD(50)));
3255  env.require(balance(ivy, EUR(100)));
3256  env.require(balance(ivy, reserve(env, 3)));
3257  env.require(offers(ivy, 0));
3258 
3259  env.require(balance(joe, USD(200)));
3260  env.require(balance(joe, EUR(100)));
3261  env.require(balance(joe, reserve(env, 3)));
3262  env.require(offers(joe, 0));
3263  }
3264  {
3265  // A trust line's QualityOut should not affect offer crossing.
3266  auto const kim = Account("kim");
3267  auto const K_BUX = kim["BUX"];
3268  auto const lex = Account("lex");
3269  auto const meg = Account("meg");
3270  auto const ned = Account("ned");
3271  auto const N_BUX = ned["BUX"];
3272 
3273  // Verify trust line QualityOut affects payments.
3274  env.fund(reserve(env, 4) + (fee * 4), kim, lex, meg, ned);
3275  env.close();
3276 
3277  env(trust(lex, K_BUX(400)));
3278  env(trust(lex, N_BUX(200)), qualityOutPercent(120));
3279  env(trust(meg, N_BUX(100)));
3280  env.close();
3281  env(pay(ned, lex, N_BUX(100)));
3282  env.close();
3283  env.require(balance(lex, N_BUX(100)));
3284 
3285  env(pay(kim, meg, N_BUX(60)), path(lex, ned), sendmax(K_BUX(200)));
3286  env.close();
3287 
3288  env.require(balance(kim, K_BUX(none)));
3289  env.require(balance(kim, N_BUX(none)));
3290  env.require(balance(lex, K_BUX(72)));
3291  env.require(balance(lex, N_BUX(40)));
3292  env.require(balance(meg, K_BUX(none)));
3293  env.require(balance(meg, N_BUX(60)));
3294  env.require(balance(ned, K_BUX(none)));
3295  env.require(balance(ned, N_BUX(none)));
3296 
3297  // Now verify that offer crossing is unaffected by QualityOut.
3298  env(offer(lex, K_BUX(30), N_BUX(30)));
3299  env.close();
3300 
3301  env(offer(kim, N_BUX(30), K_BUX(30)));
3302  env.close();
3303 
3304  env.require(balance(kim, K_BUX(none)));
3305  env.require(balance(kim, N_BUX(30)));
3306  env.require(balance(lex, K_BUX(102)));
3307  env.require(balance(lex, N_BUX(10)));
3308  env.require(balance(meg, K_BUX(none)));
3309  env.require(balance(meg, N_BUX(60)));
3310  env.require(balance(ned, K_BUX(-30)));
3311  env.require(balance(ned, N_BUX(none)));
3312  }
3313  {
3314  // Make sure things work right when we're auto-bridging as well.
3315  auto const ova = Account("ova");
3316  auto const pat = Account("pat");
3317  auto const qae = Account("qae");
3318  env.fund(XRP(2) + reserve(env, 3) + (fee * 3), ova, pat, qae);
3319  env.close();
3320 
3321  // o ova has USD but wants XPR.
3322  // o pat has XRP but wants EUR.
3323  // o qae has EUR but wants USD.
3324  env(trust(ova, USD(200)));
3325  env(trust(ova, EUR(200)));
3326  env(trust(pat, USD(200)));
3327  env(trust(pat, EUR(200)));
3328  env(trust(qae, USD(200)));
3329  env(trust(qae, EUR(200)));
3330  env.close();
3331 
3332  env(pay(gw1, ova, USD(125)));
3333  env(pay(gw2, qae, EUR(150)));
3334  env.close();
3335 
3336  env(offer(ova, XRP(2), USD(100)));
3337  env(offer(pat, EUR(100), XRP(2)));
3338  env.close();
3339 
3340  env(offer(qae, USD(100), EUR(100)));
3341  env.close();
3342 
3343  env.require(balance(ova, USD(0)));
3344  env.require(balance(ova, EUR(0)));
3345  env.require(balance(ova, XRP(4) + reserve(env, 3)));
3346 
3347  // In pre-flow code ova's offer is left empty in the ledger.
3348  auto const ovasOffers = offersOnAccount(env, ova);
3349  if (ovasOffers.size() != 0)
3350  {
3351  BEAST_EXPECT(ovasOffers.size() == 1);
3352  auto const& ovasOffer = *(ovasOffers.front());
3353 
3354  BEAST_EXPECT(ovasOffer[sfLedgerEntryType] == ltOFFER);
3355  BEAST_EXPECT(ovasOffer[sfTakerGets] == USD(0));
3356  BEAST_EXPECT(ovasOffer[sfTakerPays] == XRP(0));
3357  }
3358 
3359  env.require(balance(pat, USD(0)));
3360  env.require(balance(pat, EUR(100)));
3361  env.require(balance(pat, XRP(0) + reserve(env, 3)));
3362  env.require(offers(pat, 0));
3363 
3364  env.require(balance(qae, USD(100)));
3365  env.require(balance(qae, EUR(0)));
3366  env.require(balance(qae, XRP(2) + reserve(env, 3)));
3367  env.require(offers(qae, 0));
3368  }
3369  }
3370 
3371  void
3373  {
3374  // The following test verifies some correct but slightly surprising
3375  // behavior in offer crossing. The scenario:
3376  //
3377  // o An entity has created one or more offers.
3378  // o The entity creates another offer that can be directly crossed
3379  // (not autobridged) by the previously created offer(s).
3380  // o Rather than self crossing the offers, delete the old offer(s).
3381  //
3382  // See a more complete explanation in the comments for
3383  // BookOfferCrossingStep::limitSelfCrossQuality().
3384  //
3385  // Note that, in this particular example, one offer causes several
3386  // crossable offers (worth considerably more than the new offer)
3387  // to be removed from the book.
3388  using namespace jtx;
3389 
3390  auto const gw = Account("gateway");
3391  auto const USD = gw["USD"];
3392 
3393  Env env{*this, features};
3394 
3395  // The fee that's charged for transactions.
3396  auto const fee = env.current()->fees().base;
3397  auto const startBalance = XRP(1000000);
3398 
3399  env.fund(startBalance + (fee * 4), gw);
3400  env.close();
3401 
3402  env(offer(gw, USD(60), XRP(600)));
3403  env.close();
3404  env(offer(gw, USD(60), XRP(600)));
3405  env.close();
3406  env(offer(gw, USD(60), XRP(600)));
3407  env.close();
3408 
3409  env.require(owners(gw, 3));
3410  env.require(balance(gw, startBalance + fee));
3411 
3412  auto gwOffers = offersOnAccount(env, gw);
3413  BEAST_EXPECT(gwOffers.size() == 3);
3414  for (auto const& offerPtr : gwOffers)
3415  {
3416  auto const& offer = *offerPtr;
3417  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3418  BEAST_EXPECT(offer[sfTakerGets] == XRP(600));
3419  BEAST_EXPECT(offer[sfTakerPays] == USD(60));
3420  }
3421 
3422  // Since this offer crosses the first offers, the previous offers
3423  // will be deleted and this offer will be put on the order book.
3424  env(offer(gw, XRP(1000), USD(100)));
3425  env.close();
3426  env.require(owners(gw, 1));
3427  env.require(offers(gw, 1));
3428  env.require(balance(gw, startBalance));
3429 
3430  gwOffers = offersOnAccount(env, gw);
3431  BEAST_EXPECT(gwOffers.size() == 1);
3432  for (auto const& offerPtr : gwOffers)
3433  {
3434  auto const& offer = *offerPtr;
3435  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3436  BEAST_EXPECT(offer[sfTakerGets] == USD(100));
3437  BEAST_EXPECT(offer[sfTakerPays] == XRP(1000));
3438  }
3439  }
3440 
3441  void
3443  {
3444  using namespace jtx;
3445 
3446  auto const gw1 = Account("gateway1");
3447  auto const gw2 = Account("gateway2");
3448  auto const alice = Account("alice");
3449  auto const USD = gw1["USD"];
3450  auto const EUR = gw2["EUR"];
3451 
3452  Env env{*this, features};
3453 
3454  env.fund(XRP(1000000), gw1, gw2);
3455  env.close();
3456 
3457  // The fee that's charged for transactions.
3458  auto const f = env.current()->fees().base;
3459 
3460  // Test cases
3461  struct TestData
3462  {
3463  std::string acct; // Account operated on
3464  STAmount fundXRP; // XRP acct funded with
3465  STAmount fundUSD; // USD acct funded with
3466  STAmount fundEUR; // EUR acct funded with
3467  TER firstOfferTec; // tec code on first offer
3468  TER secondOfferTec; // tec code on second offer
3469  };
3470 
3471  // clang-format off
3472  TestData const tests[]{
3473  // acct fundXRP fundUSD fundEUR firstOfferTec secondOfferTec
3474  {"ann", reserve(env, 3) + f * 4, USD(1000), EUR(1000), tesSUCCESS, tesSUCCESS},
3475  {"bev", reserve(env, 3) + f * 4, USD( 1), EUR(1000), tesSUCCESS, tesSUCCESS},
3476  {"cam", reserve(env, 3) + f * 4, USD(1000), EUR( 1), tesSUCCESS, tesSUCCESS},
3477  {"deb", reserve(env, 3) + f * 4, USD( 0), EUR( 1), tesSUCCESS, tecUNFUNDED_OFFER},
3478  {"eve", reserve(env, 3) + f * 4, USD( 1), EUR( 0), tecUNFUNDED_OFFER, tesSUCCESS},
3479  {"flo", reserve(env, 3) + 0, USD(1000), EUR(1000), tecINSUF_RESERVE_OFFER, tecINSUF_RESERVE_OFFER},
3480  };
3481  //clang-format on
3482 
3483  for (auto const& t : tests)
3484  {
3485  auto const acct = Account{t.acct};
3486  env.fund(t.fundXRP, acct);
3487  env.close();
3488 
3489  env(trust(acct, USD(1000)));
3490  env(trust(acct, EUR(1000)));
3491  env.close();
3492 
3493  if (t.fundUSD > USD(0))
3494  env(pay(gw1, acct, t.fundUSD));
3495  if (t.fundEUR > EUR(0))
3496  env(pay(gw2, acct, t.fundEUR));
3497  env.close();
3498 
3499  env(offer(acct, USD(500), EUR(600)), ter(t.firstOfferTec));
3500  env.close();
3501  std::uint32_t const firstOfferSeq = env.seq(acct) - 1;
3502 
3503  int offerCount = t.firstOfferTec == tesSUCCESS ? 1 : 0;
3504  env.require(owners(acct, 2 + offerCount));
3505  env.require(balance(acct, t.fundUSD));
3506  env.require(balance(acct, t.fundEUR));
3507 
3508  auto acctOffers = offersOnAccount(env, acct);
3509  BEAST_EXPECT(acctOffers.size() == offerCount);
3510  for (auto const& offerPtr : acctOffers)
3511  {
3512  auto const& offer = *offerPtr;
3513  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3514  BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
3515  BEAST_EXPECT(offer[sfTakerPays] == USD(500));
3516  }
3517 
3518  env(offer(acct, EUR(600), USD(500)), ter(t.secondOfferTec));
3519  env.close();
3520  std::uint32_t const secondOfferSeq = env.seq(acct) - 1;
3521 
3522  offerCount = t.secondOfferTec == tesSUCCESS ? 1 : offerCount;
3523  env.require(owners(acct, 2 + offerCount));
3524  env.require(balance(acct, t.fundUSD));
3525  env.require(balance(acct, t.fundEUR));
3526 
3527  acctOffers = offersOnAccount(env, acct);
3528  BEAST_EXPECT(acctOffers.size() == offerCount);
3529  for (auto const& offerPtr : acctOffers)
3530  {
3531  auto const& offer = *offerPtr;
3532  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3533  if (offer[sfSequence] == firstOfferSeq)
3534  {
3535  BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
3536  BEAST_EXPECT(offer[sfTakerPays] == USD(500));
3537  }
3538  else
3539  {
3540  BEAST_EXPECT(offer[sfTakerGets] == USD(500));
3541  BEAST_EXPECT(offer[sfTakerPays] == EUR(600));
3542  }
3543  }
3544 
3545  // Remove any offers from acct for the next pass.
3546  env(offer_cancel(acct, firstOfferSeq));
3547  env.close();
3548  env(offer_cancel(acct, secondOfferSeq));
3549  env.close();
3550  }
3551  }
3552 
3553  void
3555  {
3556  testcase("Self Cross Offer");
3557  testSelfCrossOffer1(features);
3558  testSelfCrossOffer2(features);
3559  }
3560 
3561  void
3563  {
3564  // Folks who issue their own currency have, in effect, as many
3565  // funds as they are trusted for. This test used to fail because
3566  // self-issuing was not properly checked. Verify that it works
3567  // correctly now.
3568  using namespace jtx;
3569 
3570  Env env{*this, features};
3571 
3572  auto const alice = Account("alice");
3573  auto const bob = Account("bob");
3574  auto const USD = bob["USD"];
3575  auto const f = env.current()->fees().base;
3576 
3577  env.fund(XRP(50000) + f, alice, bob);
3578  env.close();
3579 
3580  env(offer(alice, USD(5000), XRP(50000)));
3581  env.close();
3582 
3583  // This offer should take alice's offer up to Alice's reserve.
3584  env(offer(bob, XRP(50000), USD(5000)));
3585  env.close();
3586 
3587  // alice's offer should have been removed, since she's down to her
3588  // XRP reserve.
3589  env.require(balance(alice, XRP(250)));
3590  env.require(owners(alice, 1));
3591  env.require(lines(alice, 1));
3592 
3593  // However bob's offer should be in the ledger, since it was not
3594  // fully crossed.
3595  auto const bobOffers = offersOnAccount(env, bob);
3596  BEAST_EXPECT(bobOffers.size() == 1);
3597  for (auto const& offerPtr : bobOffers)
3598  {
3599  auto const& offer = *offerPtr;
3600  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3601  BEAST_EXPECT(offer[sfTakerGets] == USD(25));
3602  BEAST_EXPECT(offer[sfTakerPays] == XRP(250));
3603  }
3604  }
3605 
3606  void
3608  {
3609  // At one point in the past this invalid path caused an assert. It
3610  // should not be possible for user-supplied data to cause an assert.
3611  // Make sure the assert is gone.
3612  testcase("Bad path assert");
3613 
3614  using namespace jtx;
3615 
3616  // The problem was identified when featureOwnerPaysFee was enabled,
3617  // so make sure that gets included.
3618  Env env{*this, features | featureOwnerPaysFee};
3619 
3620  // The fee that's charged for transactions.
3621  auto const fee = env.current()->fees().base;
3622  {
3623  // A trust line's QualityOut should not affect offer crossing.
3624  auto const ann = Account("ann");
3625  auto const A_BUX = ann["BUX"];
3626  auto const bob = Account("bob");
3627  auto const cam = Account("cam");
3628  auto const dan = Account("dan");
3629  auto const D_BUX = dan["BUX"];
3630 
3631  // Verify trust line QualityOut affects payments.
3632  env.fund(reserve(env, 4) + (fee * 4), ann, bob, cam, dan);
3633  env.close();
3634 
3635  env(trust(bob, A_BUX(400)));
3636  env(trust(bob, D_BUX(200)), qualityOutPercent(120));
3637  env(trust(cam, D_BUX(100)));
3638  env.close();
3639  env(pay(dan, bob, D_BUX(100)));
3640  env.close();
3641  env.require(balance(bob, D_BUX(100)));
3642 
3643  env(pay(ann, cam, D_BUX(60)), path(bob, dan), sendmax(A_BUX(200)));
3644  env.close();
3645 
3646  env.require(balance(ann, A_BUX(none)));
3647  env.require(balance(ann, D_BUX(none)));
3648  env.require(balance(bob, A_BUX(72)));
3649  env.require(balance(bob, D_BUX(40)));
3650  env.require(balance(cam, A_BUX(none)));
3651  env.require(balance(cam, D_BUX(60)));
3652  env.require(balance(dan, A_BUX(none)));
3653  env.require(balance(dan, D_BUX(none)));
3654 
3655  env(offer(bob, A_BUX(30), D_BUX(30)));
3656  env.close();
3657 
3658  env(trust(ann, D_BUX(100)));
3659  env.close();
3660 
3661  // This payment caused the assert.
3662  env(pay(ann, ann, D_BUX(30)),
3663  path(A_BUX, D_BUX),
3664  sendmax(A_BUX(30)),
3665  ter(temBAD_PATH));
3666  env.close();
3667 
3668  env.require(balance(ann, A_BUX(none)));
3669  env.require(balance(ann, D_BUX(0)));
3670  env.require(balance(bob, A_BUX(72)));
3671  env.require(balance(bob, D_BUX(40)));
3672  env.require(balance(cam, A_BUX(none)));
3673  env.require(balance(cam, D_BUX(60)));
3674  env.require(balance(dan, A_BUX(0)));
3675  env.require(balance(dan, D_BUX(none)));
3676  }
3677  }
3678 
3679  void
3681  {
3682  // The offer crossing code expects that a DirectStep is always
3683  // preceded by a BookStep. In one instance the default path
3684  // was not matching that assumption. Here we recreate that case
3685  // so we can prove the bug stays fixed.
3686  testcase("Direct to Direct path");
3687 
3688  using namespace jtx;
3689 
3690  Env env{*this, features};
3691 
3692  auto const ann = Account("ann");
3693  auto const bob = Account("bob");
3694  auto const cam = Account("cam");
3695  auto const A_BUX = ann["BUX"];
3696  auto const B_BUX = bob["BUX"];
3697 
3698  auto const fee = env.current()->fees().base;
3699  env.fund(reserve(env, 4) + (fee * 5), ann, bob, cam);
3700  env.close();
3701 
3702  env(trust(ann, B_BUX(40)));
3703  env(trust(cam, A_BUX(40)));
3704  env(trust(cam, B_BUX(40)));
3705  env.close();
3706 
3707  env(pay(ann, cam, A_BUX(35)));
3708  env(pay(bob, cam, B_BUX(35)));
3709 
3710  env(offer(bob, A_BUX(30), B_BUX(30)));
3711  env.close();
3712 
3713  // cam puts an offer on the books that her upcoming offer could cross.
3714  // But this offer should be deleted, not crossed, by her upcoming
3715  // offer.
3716  env(offer(cam, A_BUX(29), B_BUX(30), tfPassive));
3717  env.close();
3718  env.require(balance(cam, A_BUX(35)));
3719  env.require(balance(cam, B_BUX(35)));
3720  env.require(offers(cam, 1));
3721 
3722  // This offer caused the assert.
3723  env(offer(cam, B_BUX(30), A_BUX(30)));
3724  env.close();
3725 
3726  env.require(balance(bob, A_BUX(30)));
3727  env.require(balance(cam, A_BUX(5)));
3728  env.require(balance(cam, B_BUX(65)));
3729  env.require(offers(cam, 0));
3730  }
3731 
3732  void
3734  {
3735  // The Flow offer crossing code used to assert if an offer was made
3736  // for more XRP than the offering account held. This unit test
3737  // reproduces that failing case.
3738  testcase("Self crossing low quality offer");
3739 
3740  using namespace jtx;
3741 
3742  Env env{*this, features};
3743 
3744  auto const ann = Account("ann");
3745  auto const gw = Account("gateway");
3746  auto const BTC = gw["BTC"];
3747 
3748  auto const fee = env.current()->fees().base;
3749  env.fund(reserve(env, 2) + drops(9999640) + (fee), ann);
3750  env.fund(reserve(env, 2) + (fee * 4), gw);
3751  env.close();
3752 
3753  env(rate(gw, 1.002));
3754  env(trust(ann, BTC(10)));
3755  env.close();
3756 
3757  env(pay(gw, ann, BTC(2.856)));
3758  env.close();
3759 
3760  env(offer(ann, drops(365611702030), BTC(5.713)));
3761  env.close();
3762 
3763  // This offer caused the assert.
3764  env(offer(ann, BTC(0.687), drops(20000000000)),
3766  }
3767 
3768  void
3770  {
3771  // The Flow offer crossing code had a case where it was not rounding
3772  // the offer crossing correctly after a partial crossing. The
3773  // failing case was found on the network. Here we add the case to
3774  // the unit tests.
3775  testcase("Offer In Scaling");
3776 
3777  using namespace jtx;
3778 
3779  Env env{*this, features};
3780 
3781  auto const gw = Account("gateway");
3782  auto const alice = Account("alice");
3783  auto const bob = Account("bob");
3784  auto const CNY = gw["CNY"];
3785 
3786  auto const fee = env.current()->fees().base;
3787  env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
3788  env.fund(reserve(env, 2) + (fee * 4), gw);
3789  env.close();
3790 
3791  env(trust(bob, CNY(500)));
3792  env.close();
3793 
3794  env(pay(gw, bob, CNY(300)));
3795  env.close();
3796 
3797  env(offer(bob, drops(5400000000), CNY(216.054)));
3798  env.close();
3799 
3800  // This offer did not round result of partial crossing correctly.
3801  env(offer(alice, CNY(13562.0001), drops(339000000000)));
3802  env.close();
3803 
3804  auto const aliceOffers = offersOnAccount(env, alice);
3805  BEAST_EXPECT(aliceOffers.size() == 1);
3806  for (auto const& offerPtr : aliceOffers)
3807  {
3808  auto const& offer = *offerPtr;
3809  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3810  BEAST_EXPECT(offer[sfTakerGets] == drops(333599446582));
3811  BEAST_EXPECT(offer[sfTakerPays] == CNY(13345.9461));
3812  }
3813  }
3814 
3815  void
3817  {
3818  // After adding the previous case, there were still failing rounding
3819  // cases in Flow offer crossing. This one was because the gateway
3820  // transfer rate was not being correctly handled.
3821  testcase("Offer In Scaling With Xfer Rate");
3822 
3823  using namespace jtx;
3824 
3825  Env env{*this, features};
3826 
3827  auto const gw = Account("gateway");
3828  auto const alice = Account("alice");
3829  auto const bob = Account("bob");
3830  auto const BTC = gw["BTC"];
3831  auto const JPY = gw["JPY"];
3832 
3833  auto const fee = env.current()->fees().base;
3834  env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
3835  env.fund(reserve(env, 2) + (fee * 4), gw);
3836  env.close();
3837 
3838  env(rate(gw, 1.002));
3839  env(trust(alice, JPY(4000)));
3840  env(trust(bob, BTC(2)));
3841  env.close();
3842 
3843  env(pay(gw, alice, JPY(3699.034802280317)));
3844  env(pay(gw, bob, BTC(1.156722559140311)));
3845  env.close();
3846 
3847  env(offer(bob, JPY(1241.913390770747), BTC(0.01969825690469254)));
3848  env.close();
3849 
3850  // This offer did not round result of partial crossing correctly.
3851  env(offer(alice, BTC(0.05507568706427876), JPY(3472.696773391072)));
3852  env.close();
3853 
3854  auto const aliceOffers = offersOnAccount(env, alice);
3855  BEAST_EXPECT(aliceOffers.size() == 1);
3856  for (auto const& offerPtr : aliceOffers)
3857  {
3858  auto const& offer = *offerPtr;
3859  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3860  BEAST_EXPECT(
3861  offer[sfTakerGets] ==
3862  STAmount(JPY.issue(), std::uint64_t(2230682446713524ul), -12));
3863  BEAST_EXPECT(offer[sfTakerPays] == BTC(0.035378));
3864  }
3865  }
3866 
3867  void
3869  {
3870  // Another instance where Flow offer crossing was not always
3871  // working right was if the Taker had fewer funds than the Offer
3872  // was offering. The basis for this test came off the network.
3873  testcase("Offer Threshold With Reduced Funds");
3874 
3875  using namespace jtx;
3876 
3877  Env env{*this, features};
3878 
3879  auto const gw1 = Account("gw1");
3880  auto const gw2 = Account("gw2");
3881  auto const alice = Account("alice");
3882  auto const bob = Account("bob");
3883  auto const USD = gw1["USD"];
3884  auto const JPY = gw2["JPY"];
3885 
3886  auto const fee = env.current()->fees().base;
3887  env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
3888  env.fund(reserve(env, 2) + (fee * 4), gw1, gw2);
3889  env.close();
3890 
3891  env(rate(gw1, 1.002));
3892  env(trust(alice, USD(1000)));
3893  env(trust(bob, JPY(100000)));
3894  env.close();
3895 
3896  env(
3897  pay(gw1,
3898  alice,
3899  STAmount{USD.issue(), std::uint64_t(2185410179555600), -14}));
3900  env(
3901  pay(gw2,
3902  bob,
3903  STAmount{JPY.issue(), std::uint64_t(6351823459548956), -12}));
3904  env.close();
3905 
3906  env(offer(
3907  bob,
3908  STAmount{USD.issue(), std::uint64_t(4371257532306000), -17},
3909  STAmount{JPY.issue(), std::uint64_t(4573216636606000), -15}));
3910  env.close();
3911 
3912  // This offer did not partially cross correctly.
3913  env(offer(
3914  alice,
3915  STAmount{JPY.issue(), std::uint64_t(2291181510070762), -12},
3916  STAmount{USD.issue(), std::uint64_t(2190218999914694), -14}));
3917  env.close();
3918 
3919  auto const aliceOffers = offersOnAccount(env, alice);
3920  BEAST_EXPECT(aliceOffers.size() == 1);
3921  for (auto const& offerPtr : aliceOffers)
3922  {
3923  auto const& offer = *offerPtr;
3924  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
3925  BEAST_EXPECT(
3926  offer[sfTakerGets] ==
3927  STAmount(USD.issue(), std::uint64_t(2185847305256635), -14));
3928  BEAST_EXPECT(
3929  offer[sfTakerPays] ==
3930  STAmount(JPY.issue(), std::uint64_t(2286608293434156), -12));
3931  }
3932  }
3933 
3934  void
3936  {
3937  testcase("Tiny Offer");
3938 
3939  using namespace jtx;
3940 
3941  Env env{*this, features};
3942 
3943  auto const gw = Account("gw");
3944  auto const alice = Account("alice");
3945  auto const bob = Account("bob");
3946  auto const CNY = gw["CNY"];
3947  auto const fee = env.current()->fees().base;
3948  auto const startXrpBalance = drops(400000000000) + (fee * 2);
3949 
3950  env.fund(startXrpBalance, gw, alice, bob);
3951  env.close();
3952 
3953  env(trust(bob, CNY(100000)));
3954  env.close();
3955 
3956  // Place alice's tiny offer in the book first. Let's see what happens
3957  // when a reasonable offer crosses it.
3958  STAmount const alicesCnyOffer{
3959  CNY.issue(), std::uint64_t(4926000000000000), -23};
3960 
3961  env(offer(alice, alicesCnyOffer, drops(1), tfPassive));
3962  env.close();
3963 
3964  // bob places an ordinary offer
3965  STAmount const bobsCnyStartBalance{
3966  CNY.issue(), std::uint64_t(3767479960090235), -15};
3967  env(pay(gw, bob, bobsCnyStartBalance));
3968  env.close();
3969 
3970  env(offer(
3971  bob,
3972  drops(203),
3973  STAmount{CNY.issue(), std::uint64_t(1000000000000000), -20}));
3974  env.close();
3975 
3976  env.require(balance(alice, alicesCnyOffer));
3977  env.require(balance(alice, startXrpBalance - fee - drops(1)));
3978  env.require(balance(bob, bobsCnyStartBalance - alicesCnyOffer));
3979  env.require(balance(bob, startXrpBalance - (fee * 2) + drops(1)));
3980  }
3981 
3982  void
3984  {
3985  testcase("Self Pay Xfer Fee");
3986  // The old offer crossing code does not charge a transfer fee
3987  // if alice pays alice. That's different from how payments work.
3988  // Payments always charge a transfer fee even if the money is staying
3989  // in the same hands.
3990  //
3991  // What's an example where alice pays alice? There are three actors:
3992  // gw, alice, and bob.
3993  //
3994  // 1. gw issues BTC and USD. qw charges a 0.2% transfer fee.
3995  //
3996  // 2. alice makes an offer to buy XRP and sell USD.
3997  // 3. bob makes an offer to buy BTC and sell XRP.
3998  //
3999  // 4. alice now makes an offer to sell BTC and buy USD.
4000  //
4001  // This last offer crosses using auto-bridging.
4002  // o alice's last offer sells BTC to...
4003  // o bob' offer which takes alice's BTC and sells XRP to...
4004  // o alice's first offer which takes bob's XRP and sells USD to...
4005  // o alice's last offer.
4006  //
4007  // So alice sells USD to herself.
4008  //
4009  // There are six cases that we need to test:
4010  // o alice crosses her own offer on the first leg (BTC).
4011  // o alice crosses her own offer on the second leg (USD).
4012  // o alice crosses her own offers on both legs.
4013  // All three cases need to be tested:
4014  // o In reverse (alice has enough BTC to cover her offer) and
4015  // o Forward (alice owns less BTC than is in her final offer.
4016  //
4017  // It turns out that two of the forward cases fail for a different
4018  // reason. They are therefore commented out here, But they are
4019  // revisited in the testSelfPayUnlimitedFunds() unit test.
4020 
4021  using namespace jtx;
4022 
4023  Env env{*this, features};
4024 
4025  auto const gw = Account("gw");
4026  auto const BTC = gw["BTC"];
4027  auto const USD = gw["USD"];
4028  auto const startXrpBalance = XRP(4000000);
4029 
4030  env.fund(startXrpBalance, gw);
4031  env.close();
4032 
4033  env(rate(gw, 1.25));
4034  env.close();
4035 
4036  // Test cases
4037  struct Actor
4038  {
4039  Account acct;
4040  int offers; // offers on account after crossing
4041  PrettyAmount xrp; // final expected after crossing
4042  PrettyAmount btc; // final expected after crossing
4043  PrettyAmount usd; // final expected after crossing
4044  };
4045  struct TestData
4046  {
4047  // The first three three integers give the *index* in actors
4048  // to assign each of the three roles. By using indices it is
4049  // easy for alice to own the offer in the first leg, the second
4050  // leg, or both.
4051  std::size_t self;
4052  std::size_t leg0;
4053  std::size_t leg1;
4054  PrettyAmount btcStart;
4055  std::vector<Actor> actors;
4056  };
4057 
4058  // clang-format off
4059  TestData const tests[]{
4060  // btcStart --------------------- actor[0] --------------------- -------------------- actor[1] -------------------
4061  {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
4062  {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
4063  {0, 0, 0, BTC(20), {{"cam", 0, drops(3999999999950), BTC(20.0), USD(2000)} }}, // no xfer fee
4064  {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
4065  };
4066  // clang-format on
4067 
4068  for (auto const& t : tests)
4069  {
4070  Account const& self = t.actors[t.self].acct;
4071  Account const& leg0 = t.actors[t.leg0].acct;
4072  Account const& leg1 = t.actors[t.leg1].acct;
4073 
4074  for (auto const& actor : t.actors)
4075  {
4076  env.fund(XRP(4000000), actor.acct);
4077  env.close();
4078 
4079  env(trust(actor.acct, BTC(40)));
4080  env(trust(actor.acct, USD(8000)));
4081  env.close();
4082  }
4083 
4084  env(pay(gw, self, t.btcStart));
4085  env(pay(gw, self, USD(2000)));
4086  if (self.id() != leg1.id())
4087  env(pay(gw, leg1, USD(2000)));
4088  env.close();
4089 
4090  // Get the initial offers in place. Remember their sequences
4091  // so we can delete them later.
4092  env(offer(leg0, BTC(10), XRP(100000), tfPassive));
4093  env.close();
4094  std::uint32_t const leg0OfferSeq = env.seq(leg0) - 1;
4095 
4096  env(offer(leg1, XRP(100000), USD(1000), tfPassive));
4097  env.close();
4098  std::uint32_t const leg1OfferSeq = env.seq(leg1) - 1;
4099 
4100  // This is the offer that matters.
4101  env(offer(self, USD(1000), BTC(10)));
4102  env.close();
4103  std::uint32_t const selfOfferSeq = env.seq(self) - 1;
4104 
4105  // Verify results.
4106  for (auto const& actor : t.actors)
4107  {
4108  // Sometimes Taker crossing gets lazy about deleting offers.
4109  // Treat an empty offer as though it is deleted.
4110  auto actorOffers = offersOnAccount(env, actor.acct);
4111  auto const offerCount = std::distance(
4112  actorOffers.begin(),
4114  actorOffers.begin(),
4115  actorOffers.end(),
4116  [](std::shared_ptr<SLE const>& offer) {
4117  return (*offer)[sfTakerGets].signum() == 0;
4118  }));
4119  BEAST_EXPECT(offerCount == actor.offers);
4120 
4121  env.require(balance(actor.acct, actor.xrp));
4122  env.require(balance(actor.acct, actor.btc));
4123  env.require(balance(actor.acct, actor.usd));
4124  }
4125  // Remove any offers that might be left hanging around. They
4126  // could bollix up later loops.
4127  env(offer_cancel(leg0, leg0OfferSeq));
4128  env.close();
4129  env(offer_cancel(leg1, leg1OfferSeq));
4130  env.close();
4131  env(offer_cancel(self, selfOfferSeq));
4132  env.close();
4133  }
4134  }
4135 
4136  void
4138  {
4139  testcase("Self Pay Unlimited Funds");
4140  // The Taker offer crossing code recognized when Alice was paying
4141  // Alice the same denomination. In this case, as long as Alice
4142  // has a little bit of that denomination, it treats Alice as though
4143  // she has unlimited funds in that denomination.
4144  //
4145  // Huh? What kind of sense does that make?
4146  //
4147  // One way to think about it is to break a single payment into a
4148  // series of very small payments executed sequentially but very
4149  // quickly. Alice needs to pay herself 1 USD, but she only has
4150  // 0.01 USD. Alice says, "Hey Alice, let me pay you a penny."
4151  // Alice does this, taking the penny out of her pocket and then
4152  // putting it back in her pocket. Then she says, "Hey Alice,
4153  // I found another penny. I can pay you another penny." Repeat
4154  // these steps 100 times and Alice has paid herself 1 USD even though
4155  // she only owns 0.01 USD.
4156  //
4157  // That's all very nice, but the payment code does not support this
4158  // optimization. In part that's because the payment code can
4159  // operate on a whole batch of offers. As a matter of fact, it can
4160  // deal in two consecutive batches of offers. It would take a great
4161  // deal of sorting out to figure out which offers in the two batches
4162  // had the same owner and give them special processing. And,
4163  // honestly, it's a weird little corner case.
4164  //
4165  // So, since Flow offer crossing uses the payments engine, Flow
4166  // offer crossing no longer supports this optimization.
4167  //
4168  // The following test shows the difference in the behaviors between
4169  // Taker offer crossing and Flow offer crossing.
4170 
4171  using namespace jtx;
4172 
4173  Env env{*this, features};
4174 
4175  auto const gw = Account("gw");
4176  auto const BTC = gw["BTC"];
4177  auto const USD = gw["USD"];
4178  auto const startXrpBalance = XRP(4000000);
4179 
4180  env.fund(startXrpBalance, gw);
4181  env.close();
4182 
4183  env(rate(gw, 1.25));
4184  env.close();
4185 
4186  // Test cases
4187  struct Actor
4188  {
4189  Account acct;
4190  int offers; // offers on account after crossing
4191  PrettyAmount xrp; // final expected after crossing
4192  PrettyAmount btc; // final expected after crossing
4193  PrettyAmount usd; // final expected after crossing
4194  };
4195  struct TestData
4196  {
4197  // The first three three integers give the *index* in actors
4198  // to assign each of the three roles. By using indices it is
4199  // easy for alice to own the offer in the first leg, the second
4200  // leg, or both.
4201  std::size_t self;
4202  std::size_t leg0;
4203  std::size_t leg1;
4204  PrettyAmount btcStart;
4205  std::vector<Actor> actors;
4206  };
4207 
4208  // clang-format off
4209  TestData const takerTests[]{
4210  // btcStart ------------------- actor[0] -------------------- ------------------- actor[1] --------------------
4211  {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
4212  {0, 0, 0, BTC(5), {{"flo", 0, drops(3999999999950), BTC(5), USD(2000)} }} // no xfer fee
4213  };
4214 
4215  TestData const flowTests[]{
4216  // btcStart ------------------- actor[0] -------------------- ------------------- actor[1] --------------------
4217  {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
4218  {0, 0, 0, BTC(5), {{"hye", 2, drops(3999999999950), BTC(5), USD(2000)} }} // no xfer fee
4219  };
4220  // clang-format on
4221 
4222  // Pick the right tests.
4223  auto const& tests = features[featureFlowCross] ? flowTests : takerTests;
4224 
4225  for (auto const& t : tests)
4226  {
4227  Account const& self = t.actors[t.self].acct;
4228  Account const& leg0 = t.actors[t.leg0].acct;
4229  Account const& leg1 = t.actors[t.leg1].acct;
4230 
4231  for (auto const& actor : t.actors)
4232  {
4233  env.fund(XRP(4000000), actor.acct);
4234  env.close();
4235 
4236  env(trust(actor.acct, BTC(40)));
4237  env(trust(actor.acct, USD(8000)));
4238  env.close();
4239  }
4240 
4241  env(pay(gw, self, t.btcStart));
4242  env(pay(gw, self, USD(2000)));
4243  if (self.id() != leg1.id())
4244  env(pay(gw, leg1, USD(2000)));
4245  env.close();
4246 
4247  // Get the initial offers in place. Remember their sequences
4248  // so we can delete them later.
4249  env(offer(leg0, BTC(10), XRP(100000), tfPassive));
4250  env.close();
4251  std::uint32_t const leg0OfferSeq = env.seq(leg0) - 1;
4252 
4253  env(offer(leg1, XRP(100000), USD(1000), tfPassive));
4254  env.close();
4255  std::uint32_t const leg1OfferSeq = env.seq(leg1) - 1;
4256 
4257  // This is the offer that matters.
4258  env(offer(self, USD(1000), BTC(10)));
4259  env.close();
4260  std::uint32_t const selfOfferSeq = env.seq(self) - 1;
4261 
4262  // Verify results.
4263  for (auto const& actor : t.actors)
4264  {
4265  // Sometimes Taker offer crossing gets lazy about deleting
4266  // offers. Treat an empty offer as though it is deleted.
4267  auto actorOffers = offersOnAccount(env, actor.acct);
4268  auto const offerCount = std::distance(
4269  actorOffers.begin(),
4271  actorOffers.begin(),
4272  actorOffers.end(),
4273  [](std::shared_ptr<SLE const>& offer) {
4274  return (*offer)[sfTakerGets].signum() == 0;
4275  }));
4276  BEAST_EXPECT(offerCount == actor.offers);
4277 
4278  env.require(balance(actor.acct, actor.xrp));
4279  env.require(balance(actor.acct, actor.btc));
4280  env.require(balance(actor.acct, actor.usd));
4281  }
4282  // Remove any offers that might be left hanging around. They
4283  // could bollix up later loops.
4284  env(offer_cancel(leg0, leg0OfferSeq));
4285  env.close();
4286  env(offer_cancel(leg1, leg1OfferSeq));
4287  env.close();
4288  env(offer_cancel(self, selfOfferSeq));
4289  env.close();
4290  }
4291  }
4292 
4293  void
4295  {
4296  testcase("lsfRequireAuth");
4297 
4298  using namespace jtx;
4299 
4300  Env env{*this, features};
4301 
4302  auto const gw = Account("gw");
4303  auto const alice = Account("alice");
4304  auto const bob = Account("bob");
4305  auto const gwUSD = gw["USD"];
4306  auto const aliceUSD = alice["USD"];
4307  auto const bobUSD = bob["USD"];
4308 
4309  env.fund(XRP(400000), gw, alice, bob);
4310  env.close();
4311 
4312  // GW requires authorization for holders of its IOUs
4313  env(fset(gw, asfRequireAuth));
4314  env.close();
4315 
4316  // Properly set trust and have gw authorize bob and alice
4317  env(trust(gw, bobUSD(100)), txflags(tfSetfAuth));
4318  env(trust(bob, gwUSD(100)));
4319  env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
4320  env(trust(alice, gwUSD(100)));
4321  // Alice is able to place the offer since the GW has authorized her
4322  env(offer(alice, gwUSD(40), XRP(4000)));
4323  env.close();
4324 
4325  env.require(offers(alice, 1));
4326  env.require(balance(alice, gwUSD(0)));
4327 
4328  env(pay(gw, bob, gwUSD(50)));
4329  env.close();
4330 
4331  env.require(balance(bob, gwUSD(50)));
4332 
4333  // Bob's offer should cross Alice's
4334  env(offer(bob, XRP(4000), gwUSD(40)));
4335  env.close();
4336 
4337  env.require(offers(alice, 0));
4338  env.require(balance(alice, gwUSD(40)));
4339 
4340  env.require(offers(bob, 0));
4341  env.require(balance(bob, gwUSD(10)));
4342  }
4343 
4344  void
4346  {
4347  testcase("Missing Auth");
4348  // 1. alice creates an offer to acquire USD/gw, an asset for which
4349  // she does not have a trust line. At some point in the future,
4350  // gw adds lsfRequireAuth. Then, later, alice's offer is crossed.
4351  // a. With Taker alice's unauthorized offer is consumed.
4352  // b. With FlowCross alice's offer is deleted, not consumed,
4353  // since alice is not authorized to hold USD/gw.
4354  //
4355  // 2. alice tries to create an offer for USD/gw, now that gw has
4356  // lsfRequireAuth set. This time the offer create fails because
4357  // alice is not authorized to hold USD/gw.
4358  //
4359  // 3. Next, gw creates a trust line to alice, but does not set
4360  // tfSetfAuth on that trust line. alice attempts to create an
4361  // offer and again fails.
4362  //
4363  // 4. Finally, gw sets tsfSetAuth on the trust line authorizing
4364  // alice to own USD/gw. At this point alice successfully
4365  // creates and crosses an offer for USD/gw.
4366 
4367  using namespace jtx;
4368 
4369  Env env{*this, features};
4370 
4371  auto const gw = Account("gw");
4372  auto const alice = Account("alice");
4373  auto const bob = Account("bob");
4374  auto const gwUSD = gw["USD"];
4375  auto const aliceUSD = alice["USD"];
4376  auto const bobUSD = bob["USD"];
4377 
4378  env.fund(XRP(400000), gw, alice, bob);
4379  env.close();
4380 
4381  env(offer(alice, gwUSD(40), XRP(4000)));
4382  env.close();
4383 
4384  env.require(offers(alice, 1));
4385  env.require(balance(alice, gwUSD(none)));
4386  env(fset(gw, asfRequireAuth));
4387  env.close();
4388 
4389  env(trust(gw, bobUSD(100)), txflags(tfSetfAuth));
4390  env.close();
4391  env(trust(bob, gwUSD(100)));
4392  env.close();
4393 
4394  env(pay(gw, bob, gwUSD(50)));
4395  env.close();
4396  env.require(balance(bob, gwUSD(50)));
4397 
4398  // gw now requires authorization and bob has gwUSD(50). Let's see if
4399  // bob can cross alice's offer.
4400  //
4401  // o With Taker bob's offer should cross alice's.
4402  // o With FlowCross bob's offer shouldn't cross and alice's
4403  // unauthorized offer should be deleted.
4404  env(offer(bob, XRP(4000), gwUSD(40)));
4405  env.close();
4406  std::uint32_t const bobOfferSeq = env.seq(bob) - 1;
4407 
4408  bool const flowCross = features[featureFlowCross];
4409 
4410  env.require(offers(alice, 0));
4411  if (flowCross)
4412  {
4413  // alice's unauthorized offer is deleted & bob's offer not crossed.
4414  env.require(balance(alice, gwUSD(none)));
4415  env.require(offers(bob, 1));
4416  env.require(balance(bob, gwUSD(50)));
4417  }
4418  else
4419  {
4420  // alice's offer crosses bob's
4421  env.require(balance(alice, gwUSD(40)));
4422  env.require(offers(bob, 0));
4423  env.require(balance(bob, gwUSD(10)));
4424 
4425  // The rest of the test verifies FlowCross behavior.
4426  return;
4427  }
4428 
4429  // See if alice can create an offer without authorization. alice
4430  // should not be able to create the offer and bob's offer should be
4431  // untouched.
4432  env(offer(alice, gwUSD(40), XRP(4000)), ter(tecNO_LINE));
4433  env.close();
4434 
4435  env.require(offers(alice, 0));
4436  env.require(balance(alice, gwUSD(none)));
4437 
4438  env.require(offers(bob, 1));
4439  env.require(balance(bob, gwUSD(50)));
4440 
4441  // Set up a trust line for alice, but don't authorize it. alice
4442  // should still not be able to create an offer for USD/gw.
4443  env(trust(gw, aliceUSD(100)));
4444  env.close();
4445 
4446  env(offer(alice, gwUSD(40), XRP(4000)), ter(tecNO_AUTH));
4447  env.close();
4448 
4449  env.require(offers(alice, 0));
4450  env.require(balance(alice, gwUSD(0)));
4451 
4452  env.require(offers(bob, 1));
4453  env.require(balance(bob, gwUSD(50)));
4454 
4455  // Delete bob's offer so alice can create an offer without crossing.
4456  env(offer_cancel(bob, bobOfferSeq));
4457  env.close();
4458  env.require(offers(bob, 0));
4459 
4460  // Finally, set up an authorized trust line for alice. Now alice's
4461  // offer should succeed. Note that, since this is an offer rather
4462  // than a payment, alice does not need to set a trust line limit.
4463  env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
4464  env.close();
4465 
4466  env(offer(alice, gwUSD(40), XRP(4000)));
4467  env.close();
4468 
4469  env.require(offers(alice, 1));
4470 
4471  // Now bob creates his offer again. alice's offer should cross.
4472  env(offer(bob, XRP(4000), gwUSD(40)));
4473  env.close();
4474 
4475  env.require(offers(alice, 0));
4476  env.require(balance(alice, gwUSD(40)));
4477 
4478  env.require(offers(bob, 0));
4479  env.require(balance(bob, gwUSD(10)));
4480  }
4481 
4482  void
4484  {
4485  testcase("RippleConnect Smoketest payment flow");
4486  using namespace jtx;
4487 
4488  Env env{*this, features};
4489 
4490  // This test mimics the payment flow used in the Ripple Connect
4491  // smoke test. The players:
4492  // A USD gateway with hot and cold wallets
4493  // A EUR gateway with hot and cold walllets
4494  // A MM gateway that will provide offers from USD->EUR and EUR->USD
4495  // A path from hot US to cold EUR is found and then used to send
4496  // USD for EUR that goes through the market maker
4497 
4498  auto const hotUS = Account("hotUS");
4499  auto const coldUS = Account("coldUS");
4500  auto const hotEU = Account("hotEU");
4501  auto const coldEU = Account("coldEU");
4502  auto const mm = Account("mm");
4503 
4504  auto const USD = coldUS["USD"];
4505  auto const EUR = coldEU["EUR"];
4506 
4507  env.fund(XRP(100000), hotUS, coldUS, hotEU, coldEU, mm);
4508  env.close();
4509 
4510  // Cold wallets require trust but will ripple by default
4511  for (auto const& cold : {coldUS, coldEU})
4512  {
4513  env(fset(cold, asfRequireAuth));
4514  env(fset(cold, asfDefaultRipple));
4515  }
4516  env.close();
4517 
4518  // Each hot wallet trusts the related cold wallet for a large amount
4519  env(trust(hotUS, USD(10000000)), txflags(tfSetNoRipple));
4520  env(trust(hotEU, EUR(10000000)), txflags(tfSetNoRipple));
4521  // Market maker trusts both cold wallets for a large amount
4522  env(trust(mm, USD(10000000)), txflags(tfSetNoRipple));
4523  env(trust(mm, EUR(10000000)), txflags(tfSetNoRipple));
4524  env.close();
4525 
4526  // Gateways authorize the trustlines of hot and market maker
4527  env(trust(coldUS, USD(0), hotUS, tfSetfAuth));
4528  env(trust(coldEU, EUR(0), hotEU, tfSetfAuth));
4529  env(trust(coldUS, USD(0), mm, tfSetfAuth));
4530  env(trust(coldEU, EUR(0), mm, tfSetfAuth));
4531  env.close();
4532 
4533  // Issue currency from cold wallets to hot and market maker
4534  env(pay(coldUS, hotUS, USD(5000000)));
4535  env(pay(coldEU, hotEU, EUR(5000000)));
4536  env(pay(coldUS, mm, USD(5000000)));
4537  env(pay(coldEU, mm, EUR(5000000)));
4538  env.close();
4539 
4540  // MM places offers
4541  float const rate = 0.9f; // 0.9 USD = 1 EUR
4542  env(offer(mm, EUR(4000000 * rate), USD(4000000)),
4543  json(jss::Flags, tfSell));
4544 
4545  float const reverseRate = 1.0f / rate * 1.00101f;
4546  env(offer(mm, USD(4000000 * reverseRate), EUR(4000000)),
4547  json(jss::Flags, tfSell));
4548  env.close();
4549 
4550  // There should be a path available from hot US to cold EUR
4551  {
4552  Json::Value jvParams;
4553  jvParams[jss::destination_account] = coldEU.human();
4554  jvParams[jss::destination_amount][jss::issuer] = coldEU.human();
4555  jvParams[jss::destination_amount][jss::currency] = "EUR";
4556  jvParams[jss::destination_amount][jss::value] = 10;
4557  jvParams[jss::source_account] = hotUS.human();
4558 
4559  Json::Value const jrr{env.rpc(
4560  "json", "ripple_path_find", to_string(jvParams))[jss::result]};
4561 
4562  BEAST_EXPECT(jrr[jss::status] == "success");
4563  BEAST_EXPECT(
4564  jrr[jss::alternatives].isArray() &&
4565  jrr[jss::alternatives].size() > 0);
4566  }
4567  // Send the payment using the found path.
4568  env(pay(hotUS, coldEU, EUR(10)), sendmax(USD(11.1223326)));
4569  }
4570 
4571  void
4573  {
4574  testcase("Self Auth");
4575 
4576  using namespace jtx;
4577 
4578  Env env{*this, features};
4579 
4580  auto const gw = Account("gw");
4581  auto const alice = Account("alice");
4582  auto const gwUSD = gw["USD"];
4583  auto const aliceUSD = alice["USD"];
4584 
4585  env.fund(XRP(400000), gw, alice);
4586  env.close();
4587 
4588  // Test that gw can create an offer to buy gw's currency.
4589  env(offer(gw, gwUSD(40), XRP(4000)));
4590  env.close();
4591  std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
4592  env.require(offers(gw, 1));
4593 
4594  // Since gw has an offer out, gw should not be able to set RequireAuth.
4595  env(fset(gw, asfRequireAuth), ter(tecOWNERS));
4596  env.close();
4597 
4598  // Cancel gw's offer so we can set RequireAuth.
4599  env(offer_cancel(gw, gwOfferSeq));
4600  env.close();
4601  env.require(offers(gw, 0));
4602 
4603  // gw now requires authorization for holders of its IOUs
4604  env(fset(gw, asfRequireAuth));
4605  env.close();
4606 
4607  // The test behaves differently with or without DepositPreauth.
4608  bool const preauth = features[featureDepositPreauth];
4609 
4610  // Before DepositPreauth an account with lsfRequireAuth set could not
4611  // create an offer to buy their own currency. After DepositPreauth
4612  // they can.
4613  env(offer(gw, gwUSD(40), XRP(4000)),
4614  ter(preauth ? TER{tesSUCCESS} : TER{tecNO_LINE}));
4615  env.close();
4616 
4617  env.require(offers(gw, preauth ? 1 : 0));
4618 
4619  if (!preauth)
4620  // The rest of the test verifies DepositPreauth behavior.
4621  return;
4622 
4623  // Set up an authorized trust line and pay alice gwUSD 50.
4624  env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
4625  env(trust(alice, gwUSD(100)));
4626  env.close();
4627 
4628  env(pay(gw, alice, gwUSD(50)));
4629  env.close();
4630 
4631  env.require(balance(alice, gwUSD(50)));
4632 
4633  // alice's offer should cross gw's
4634  env(offer(alice, XRP(4000), gwUSD(40)));
4635  env.close();
4636 
4637  env.require(offers(alice, 0));
4638  env.require(balance(alice, gwUSD(10)));
4639 
4640  env.require(offers(gw, 0));
4641  }
4642 
4643  void
4645  {
4646  // Show that an offer who's issuer has been deleted cannot be crossed.
4647  using namespace jtx;
4648 
4649  testcase("Deleted offer issuer");
4650 
4651  auto trustLineExists = [](jtx::Env const& env,
4652  jtx::Account const& src,
4653  jtx::Account const& dst,
4654  Currency const& cur) -> bool {
4655  return bool(env.le(keylet::line(src, dst, cur)));
4656  };
4657 
4658  Account const alice("alice");
4659  Account const becky("becky");
4660  Account const carol("carol");
4661  Account const gw("gateway");
4662  auto const USD = gw["USD"];
4663  auto const BUX = alice["BUX"];
4664 
4665  Env env{*this, features};
4666 
4667  env.fund(XRP(10000), alice, becky, carol, noripple(gw));
4668  env.trust(USD(1000), becky);
4669  env(pay(gw, becky, USD(5)));
4670  env.close();
4671  BEAST_EXPECT(trustLineExists(env, gw, becky, USD.currency));
4672 
4673  // Make offers that produce USD and can be crossed two ways:
4674  // direct XRP -> USD
4675  // direct BUX -> USD
4676  env(offer(becky, XRP(2), USD(2)), txflags(tfPassive));
4677  std::uint32_t const beckyBuxUsdSeq{env.seq(becky)};
4678  env(offer(becky, BUX(3), USD(3)), txflags(tfPassive));
4679  env.close();
4680 
4681  // becky keeps the offers, but removes the trustline.
4682  env(pay(becky, gw, USD(5)));
4683  env.trust(USD(0), becky);
4684  env.close();
4685  BEAST_EXPECT(!trustLineExists(env, gw, becky, USD.currency));
4686  BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
4687  BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
4688 
4689  // Delete gw's account.
4690  {
4691  // The ledger sequence needs to far enough ahead of the account
4692  // sequence before the account can be deleted.
4693  int const delta =
4694  [&env, &gw, openLedgerSeq = env.current()->seq()]() -> int {
4695  std::uint32_t const gwSeq{env.seq(gw)};
4696  if (gwSeq + 255 > openLedgerSeq)
4697  return gwSeq - openLedgerSeq + 255;
4698  return 0;
4699  }();
4700 
4701  for (int i = 0; i < delta; ++i)
4702  env.close();
4703 
4704  // Account deletion has a high fee. Account for that.
4705  env(acctdelete(gw, alice),
4706  fee(drops(env.current()->fees().increment)));
4707  env.close();
4708 
4709  // Verify that gw's account root is gone from the ledger.
4710  BEAST_EXPECT(!env.closed()->exists(keylet::account(gw.id())));
4711  }
4712 
4713  // alice crosses becky's first offer. The offer create fails because
4714  // the USD issuer is not in the ledger.
4715  env(offer(alice, USD(2), XRP(2)), ter(tecNO_ISSUER));
4716  env.close();
4717  env.require(offers(alice, 0));
4718  BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
4719  BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
4720 
4721  // alice crosses becky's second offer. Again, the offer create fails
4722  // because the USD issuer is not in the ledger.
4723  env(offer(alice, USD(3), BUX(3)), ter(tecNO_ISSUER));
4724  env.require(offers(alice, 0));
4725  BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
4726  BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
4727 
4728  // Cancel becky's BUX -> USD offer so we can try auto-bridging.
4729  env(offer_cancel(becky, beckyBuxUsdSeq));
4730  env.close();
4731  BEAST_EXPECT(!isOffer(env, becky, BUX(3), USD(3)));
4732 
4733  // alice creates an offer that can be auto-bridged with becky's
4734  // remaining offer.
4735  env.trust(BUX(1000), carol);
4736  env(pay(alice, carol, BUX(2)));
4737 
4738  env(offer(alice, BUX(2), XRP(2)));
4739  env.close();
4740 
4741  // carol attempts the auto-bridge. Again, the offer create fails
4742  // because the USD issuer is not in the ledger.
4743  env(offer(carol, USD(2), BUX(2)), ter(tecNO_ISSUER));
4744  env.close();
4745  BEAST_EXPECT(isOffer(env, alice, BUX(2), XRP(2)));
4746  BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
4747  }
4748 
4749  void
4751  {
4752  testcase("Tick Size");
4753 
4754  using namespace jtx;
4755 
4756  // Try to set tick size out of range
4757  {
4758  Env env{*this, features};
4759  auto const gw = Account{"gateway"};
4760  env.fund(XRP(10000), gw);
4761 
4762  auto txn = noop(gw);
4763  txn[sfTickSize.fieldName] = Quality::minTickSize - 1;
4764  env(txn, ter(temBAD_TICK_SIZE));
4765 
4766  txn[sfTickSize.fieldName] = Quality::minTickSize;
4767  env(txn);
4768  BEAST_EXPECT((*env.le(gw))[sfTickSize] == Quality::minTickSize);
4769 
4770  txn = noop(gw);
4771  txn[sfTickSize.fieldName] = Quality::maxTickSize;
4772  env(txn);
4773  BEAST_EXPECT(!env.le(gw)->isFieldPresent(sfTickSize));
4774 
4775  txn = noop(gw);
4776  txn[sfTickSize.fieldName] = Quality::maxTickSize - 1;
4777  env(txn);
4778  BEAST_EXPECT((*env.le(gw))[sfTickSize] == Quality::maxTickSize - 1);
4779 
4780  txn = noop(gw);
4781  txn[sfTickSize.fieldName] = Quality::maxTickSize + 1;
4782  env(txn, ter(temBAD_TICK_SIZE));
4783 
4784  txn[sfTickSize.fieldName] = 0;
4785  env(txn);
4786  BEAST_EXPECT(!env.le(gw)->isFieldPresent(sfTickSize));
4787  }
4788 
4789  Env env{*this, features};
4790  auto const gw = Account{"gateway"};
4791  auto const alice = Account{"alice"};
4792  auto const XTS = gw["XTS"];
4793  auto const XXX = gw["XXX"];
4794 
4795  env.fund(XRP(10000), gw, alice);
4796 
4797  {
4798  // Gateway sets its tick size to 5
4799  auto txn = noop(gw);
4800  txn[sfTickSize.fieldName] = 5;
4801  env(txn);
4802  BEAST_EXPECT((*env.le(gw))[sfTickSize] == 5);
4803  }
4804 
4805  env(trust(alice, XTS(1000)));
4806  env(trust(alice, XXX(1000)));
4807 
4808  env(pay(gw, alice, alice["XTS"](100)));
4809  env(pay(gw, alice, alice["XXX"](100)));
4810 
4811  env(offer(alice, XTS(10), XXX(30)));
4812  env(offer(alice, XTS(30), XXX(10)));
4813  env(offer(alice, XTS(10), XXX(30)), json(jss::Flags, tfSell));
4814  env(offer(alice, XTS(30), XXX(10)), json(jss::Flags, tfSell));
4815 
4817  forEachItem(
4818  *env.current(), alice, [&](std::shared_ptr<SLE const> const& sle) {
4819  if (sle->getType() == ltOFFER)
4820  offers.emplace(
4821  (*sle)[sfSequence],
4822  std::make_pair(
4823  (*sle)[sfTakerPays], (*sle)[sfTakerGets]));
4824  });
4825 
4826  // first offer
4827  auto it = offers.begin();
4828  BEAST_EXPECT(it != offers.end());
4829  BEAST_EXPECT(
4830  it->second.first == XTS(10) && it->second.second < XXX(30) &&
4831  it->second.second > XXX(29.9994));
4832 
4833  // second offer
4834  ++it;
4835  BEAST_EXPECT(it != offers.end());
4836  BEAST_EXPECT(
4837  it->second.first == XTS(30) && it->second.second == XXX(10));
4838 
4839  // third offer
4840  ++it;
4841  BEAST_EXPECT(it != offers.end());
4842  BEAST_EXPECT(
4843  it->second.first == XTS(10.0002) && it->second.second == XXX(30));
4844 
4845  // fourth offer
4846  // exact TakerPays is XTS(1/.033333)
4847  ++it;
4848  BEAST_EXPECT(it != offers.end());
4849  BEAST_EXPECT(
4850  it->second.first == XTS(30) && it->second.second == XXX(10));
4851 
4852  BEAST_EXPECT(++it == offers.end());
4853  }
4854 
4855  // Helper function that returns offers on an account sorted by sequence.
4858  {
4860  offersOnAccount(env, acct)};
4861  std::sort(
4862  offers.begin(),
4863  offers.end(),
4864  [](std::shared_ptr<SLE const> const& rhs,
4865  std::shared_ptr<SLE const> const& lhs) {
4866  return (*rhs)[sfSequence] < (*lhs)[sfSequence];
4867  });
4868  return offers;
4869  }
4870 
4871  void
4873  {
4874  testcase("Ticket Offers");
4875 
4876  using namespace jtx;
4877 
4878  // Two goals for this test.
4879  //
4880  // o Verify that offers can be created using tickets.
4881  //
4882  // o Show that offers in the _same_ order book remain in
4883  // chronological order regardless of sequence/ticket numbers.
4884  Env env{*this, features};
4885  auto const gw = Account{"gateway"};
4886  auto const alice = Account{"alice"};
4887  auto const bob = Account{"bob"};
4888  auto const USD = gw["USD"];
4889 
4890  env.fund(XRP(10000), gw, alice, bob);
4891  env.close();
4892 
4893  env(trust(alice, USD(1000)));
4894  env(trust(bob, USD(1000)));
4895  env.close();
4896 
4897  env(pay(gw, alice, USD(200)));
4898  env.close();
4899 
4900  // Create four offers from the same account with identical quality
4901  // so they go in the same order book. Each offer goes in a different
4902  // ledger so the chronology is clear.
4903  std::uint32_t const offerId_0{env.seq(alice)};
4904  env(offer(alice, XRP(50), USD(50)));
4905  env.close();
4906 
4907  // Create two tickets.
4908  std::uint32_t const ticketSeq{env.seq(alice) + 1};
4909  env(ticket::create(alice, 2));
4910  env.close();
4911 
4912  // Create another sequence-based offer.
4913  std::uint32_t const offerId_1{env.seq(alice)};
4914  BEAST_EXPECT(offerId_1 == offerId_0 + 4);
4915  env(offer(alice, XRP(50), USD(50)));
4916  env.close();
4917 
4918  // Create two ticket based offers in reverse order.
4919  std::uint32_t const offerId_2{ticketSeq + 1};
4920  env(offer(alice, XRP(50), USD(50)), ticket::use(offerId_2));
4921  env.close();
4922 
4923  // Create the last offer.
4924  std::uint32_t const offerId_3{ticketSeq};
4925  env(offer(alice, XRP(50), USD(50)), ticket::use(offerId_3));
4926  env.close();
4927 
4928  // Verify that all of alice's offers are present.
4929  {
4930  auto offers = sortedOffersOnAccount(env, alice);
4931  BEAST_EXPECT(offers.size() == 4);
4932  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_0);
4933  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_3);
4934  BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerId_2);
4935  BEAST_EXPECT(offers[3]->getFieldU32(sfSequence) == offerId_1);
4936  env.require(balance(alice, USD(200)));
4937  env.require(owners(alice, 5));
4938  }
4939 
4940  // Cross alice's first offer.
4941  env(offer(bob, USD(50), XRP(50)));
4942  env.close();
4943 
4944  // Verify that the first offer alice created was consumed.
4945  {
4946  auto offers = sortedOffersOnAccount(env, alice);
4947  BEAST_EXPECT(offers.size() == 3);
4948  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
4949  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_2);
4950  BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerId_1);
4951  }
4952 
4953  // Cross alice's second offer.
4954  env(offer(bob, USD(50), XRP(50)));
4955  env.close();
4956 
4957  // Verify that the second offer alice created was consumed.
4958  {
4959  auto offers = sortedOffersOnAccount(env, alice);
4960  BEAST_EXPECT(offers.size() == 2);
4961  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
4962  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_2);
4963  }
4964 
4965  // Cross alice's third offer.
4966  env(offer(bob, USD(50), XRP(50)));
4967  env.close();
4968 
4969  // Verify that the third offer alice created was consumed.
4970  {
4971  auto offers = sortedOffersOnAccount(env, alice);
4972  BEAST_EXPECT(offers.size() == 1);
4973  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
4974  }
4975 
4976  // Cross alice's last offer.
4977  env(offer(bob, USD(50), XRP(50)));
4978  env.close();
4979 
4980  // Verify that the third offer alice created was consumed.
4981  {
4982  auto offers = sortedOffersOnAccount(env, alice);
4983  BEAST_EXPECT(offers.size() == 0);
4984  }
4985  env.require(balance(alice, USD(0)));
4986  env.require(owners(alice, 1));
4987  env.require(balance(bob, USD(200)));
4988  env.require(owners(bob, 1));
4989  }
4990 
4991  void
4993  {
4994  testcase("Ticket Cancel Offers");
4995 
4996  using namespace jtx;
4997 
4998  // Verify that offers created with or without tickets can be canceled
4999  // by transactions with or without tickets.
5000  Env env{*this, features};
5001  auto const gw = Account{"gateway"};
5002  auto const alice = Account{"alice"};
5003  auto const USD = gw["USD"];
5004 
5005  env.fund(XRP(10000), gw, alice);
5006  env.close();
5007 
5008  env(trust(alice, USD(1000)));
5009  env.close();
5010  env.require(owners(alice, 1), tickets(alice, 0));
5011 
5012  env(pay(gw, alice, USD(200)));
5013  env.close();
5014 
5015  // Create the first of four offers using a sequence.
5016  std::uint32_t const offerSeqId_0{env.seq(alice)};
5017  env(offer(alice, XRP(50), USD(50)));
5018  env.close();
5019  env.require(owners(alice, 2), tickets(alice, 0));
5020 
5021  // Create four tickets.
5022  std::uint32_t const ticketSeq{env.seq(alice) + 1};
5023  env(ticket::create(alice, 4));
5024  env.close();
5025  env.require(owners(alice, 6), tickets(alice, 4));
5026 
5027  // Create the second (also sequence-based) offer.
5028  std::uint32_t const offerSeqId_1{env.seq(alice)};
5029  BEAST_EXPECT(offerSeqId_1 == offerSeqId_0 + 6);
5030  env(offer(alice, XRP(50), USD(50)));
5031  env.close();
5032 
5033  // Create the third (ticket-based) offer.
5034  std::uint32_t const offerTixId_0{ticketSeq + 1};
5035  env(offer(alice, XRP(50), USD(50)), ticket::use(offerTixId_0));
5036  env.close();
5037 
5038  // Create the last offer.
5039  std::uint32_t const offerTixId_1{ticketSeq};
5040  env(offer(alice, XRP(50), USD(50)), ticket::use(offerTixId_1));
5041  env.close();
5042 
5043  // Verify that all of alice's offers are present.
5044  {
5045  auto offers = sortedOffersOnAccount(env, alice);
5046  BEAST_EXPECT(offers.size() == 4);
5047  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerSeqId_0);
5048  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerTixId_1);
5049  BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerTixId_0);
5050  BEAST_EXPECT(offers[3]->getFieldU32(sfSequence) == offerSeqId_1);
5051  env.require(balance(alice, USD(200)));
5052  env.require(owners(alice, 7));
5053  }
5054 
5055  // Use a ticket to cancel an offer created with a sequence.
5056  env(offer_cancel(alice, offerSeqId_0), ticket::use(ticketSeq + 2));
5057  env.close();
5058 
5059  // Verify that offerSeqId_0 was canceled.
5060  {
5061  auto offers = sortedOffersOnAccount(env, alice);
5062  BEAST_EXPECT(offers.size() == 3);
5063  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerTixId_1);
5064  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerTixId_0);
5065  BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerSeqId_1);
5066  }
5067 
5068  // Use a ticket to cancel an offer created with a ticket.
5069  env(offer_cancel(alice, offerTixId_0), ticket::use(ticketSeq + 3));
5070  env.close();
5071 
5072  // Verify that offerTixId_0 was canceled.
5073  {
5074  auto offers = sortedOffersOnAccount(env, alice);
5075  BEAST_EXPECT(offers.size() == 2);
5076  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerTixId_1);
5077  BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerSeqId_1);
5078  }
5079 
5080  // All of alice's tickets should now be used up.
5081  env.require(owners(alice, 3), tickets(alice, 0));
5082 
5083  // Use a sequence to cancel an offer created with a ticket.
5084  env(offer_cancel(alice, offerTixId_1));
5085  env.close();
5086 
5087  // Verify that offerTixId_1 was canceled.
5088  {
5089  auto offers = sortedOffersOnAccount(env, alice);
5090  BEAST_EXPECT(offers.size() == 1);
5091  BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerSeqId_1);
5092  }
5093 
5094  // Use a sequence to cancel an offer created with a sequence.
5095  env(offer_cancel(alice, offerSeqId_1));
5096  env.close();
5097 
5098  // Verify that offerSeqId_1 was canceled.
5099  // All of alice's tickets should now be used up.
5100  env.require(owners(alice, 1), tickets(alice, 0), offers(alice, 0));
5101  }
5102 
5103  void
5105  {
5106  // An assert was falsely triggering when computing rates for offers.
5107  // This unit test would trigger that assert (which has been removed).
5108  testcase("incorrect assert fixed");
5109  using namespace jtx;
5110 
5111  Env env{*this};
5112  auto const alice = Account("alice");
5113  auto const USD = alice["USD"];
5114 
5115  env.fund(XRP(10000), alice);
5116  env.close();
5117  env(offer(alice, XRP(100000000000), USD(100000000)));
5118  pass();
5119  }
5120 
5121  void
5123  {
5124  testCanceledOffer(features);
5125  testRmFundedOffer(features);
5126  testTinyPayment(features);
5127  testXRPTinyPayment(features);
5128  testEnforceNoRipple(features);
5129  testInsufficientReserve(features);
5130  testFillModes(features);
5131  testMalformed(features);
5132  testExpiration(features);
5133  testUnfundedCross(features);
5134  testSelfCross(false, features);
5135  testSelfCross(true, features);
5136  testNegativeBalance(features);
5137  testOfferCrossWithXRP(true, features);
5138  testOfferCrossWithXRP(false, features);
5140  testOfferAcceptThenCancel(features);
5141  testOfferCancelPastAndFuture(features);
5142  testCurrencyConversionEntire(features);
5145  testCrossCurrencyStartXRP(features);
5146  testCrossCurrencyEndXRP(features);
5147  testCrossCurrencyBridged(features);
5148  testBridgedSecondLegDry(features);
5149  testOfferFeesConsumeFunds(features);
5150  testOfferCreateThenCross(features);
5151  testSellFlagBasic(features);
5152  testSellFlagExceedLimit(features);
5153  testGatewayCrossCurrency(features);
5154  testPartialCross(features);
5155  testXRPDirectCross(features);
5156  testDirectCross(features);
5157  testBridgedCross(features);
5158  testSellOffer(features);
5159  testSellWithFillOrKill(features);
5160  testTransferRateOffer(features);
5161  testSelfCrossOffer(features);
5162  testSelfIssueOffer(features);
5163  testBadPathAssert(features);
5164  testDirectToDirectPath(features);
5165  testSelfCrossLowQualityOffer(features);
5166  testOfferInScaling(features);
5169  testTinyOffer(features);
5170  testSelfPayXferFeeOffer(features);
5171  testSelfPayUnlimitedFunds(features);
5172  testRequireAuth(features);
5173  testMissingAuth(features);
5174  testRCSmoketest(features);
5175  testSelfAuth(features);
5176  testDeletedOfferIssuer(features);
5177  testTickSize(features);
5178  testTicketOffer(features);
5179  testTicketCancelOffer(features);
5182  }
5183 
5184  void
5185  run() override
5186  {
5187  using namespace jtx;
5189  FeatureBitset const flowCross{featureFlowCross};
5190  FeatureBitset const takerDryOffer{fixTakerDryOfferRemoval};
5191  FeatureBitset const rmSmallIncreasedQOffers{fixRmSmallIncreasedQOffers};
5192  FeatureBitset const immediateOfferKilled{featureImmediateOfferKilled};
5193 
5194  testAll(all - takerDryOffer - immediateOfferKilled);
5195  testAll(all - flowCross - takerDryOffer - immediateOfferKilled);
5196  testAll(all - flowCross - immediateOfferKilled);
5197  testAll(all - rmSmallIncreasedQOffers - immediateOfferKilled);
5198  testAll(all);
5199  testFalseAssert();
5200  }
5201 };
5202 
5204 {
5205  void
5206  run() override
5207  {
5208  using namespace jtx;
5210  FeatureBitset const flowCross{featureFlowCross};
5211  FeatureBitset const f1513{fix1513};
5212  FeatureBitset const immediateOfferKilled{featureImmediateOfferKilled};
5213  FeatureBitset const takerDryOffer{fixTakerDryOfferRemoval};
5214 
5215  testAll(all - flowCross - f1513 - immediateOfferKilled);
5216  testAll(all - flowCross - immediateOfferKilled);
5217  testAll(all - immediateOfferKilled);
5218  testAll(all);
5219 
5220  testAll(all - flowCross - takerDryOffer);
5221  }
5222 };
5223 
5225 BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Offer_manual, tx, ripple, 20);
5226 
5227 } // namespace test
5228 } // 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:3769
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:2232
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:2693
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:3868
ripple::test::Offer_test::verifyDefaultTrustline
void verifyDefaultTrustline(jtx::Env &env, jtx::Account const &account, jtx::PrettyAmount const &expectBalance)
Definition: Offer_test.cpp:2309
ripple::test::Offer_manual_test
Definition: Offer_test.cpp:5203
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:4857
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:4872
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:5185
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:109
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:3554
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:4137
ripple::test::Offer_test::testSelfCrossLowQualityOffer
void testSelfCrossLowQualityOffer(FeatureBitset features)
Definition: Offer_test.cpp:3733
ripple::tfPassive
constexpr std::uint32_t tfPassive
Definition: TxFlags.h:93
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:4483
ripple::test::Offer_test::testMissingAuth
void testMissingAuth(FeatureBitset features)
Definition: Offer_test.cpp:4345
ripple::test::Offer_test::testFalseAssert
void testFalseAssert()
Definition: Offer_test.cpp:5104
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:2340
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:4294
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:3816
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:102
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:2188
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:2791
ripple::tfFillOrKill
constexpr std::uint32_t tfFillOrKill
Definition: TxFlags.h:95
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:3680
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:96
ripple::test::Offer_test::testSelfIssueOffer
void testSelfIssueOffer(FeatureBitset features)
Definition: Offer_test.cpp:3562
ripple::featureImmediateOfferKilled
const uint256 featureImmediateOfferKilled
ripple::test::jtx::flags
Match set account flags.
Definition: flags.h:108
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:108
ripple::test::Offer_manual_test::run
void run() override
Definition: Offer_test.cpp:5206
ripple::test::Offer_test::testAll
void testAll(FeatureBitset features)
Definition: Offer_test.cpp:5122
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:3935
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:2498
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:3442
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:2575
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:3983
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:3607
ripple::featureOwnerPaysFee
const uint256 featureOwnerPaysFee
ripple::test::Offer_test::testSellWithFillOrKill
void testSellWithFillOrKill(FeatureBitset features)
Definition: Offer_test.cpp:2979
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:4644
ripple::test::Offer_test::testTickSize
void testTickSize(FeatureBitset features)
Definition: Offer_test.cpp:4750
ripple::temBAD_TICK_SIZE
@ temBAD_TICK_SIZE
Definition: TER.h:116
ripple::test::Offer_test::testTicketCancelOffer
void testTicketCancelOffer(FeatureBitset features)
Definition: Offer_test.cpp:4992
ripple::test::Offer_test::testTransferRateOffer
void testTransferRateOffer(FeatureBitset features)
Definition: Offer_test.cpp:3064
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:3372
ripple::test::Offer_test::testSelfAuth
void testSelfAuth(FeatureBitset features)
Definition: Offer_test.cpp:4572
ripple::featureFlowCross
const uint256 featureFlowCross
ripple::test::Offer_test::testSellFlagBasic
void testSellFlagBasic(FeatureBitset features)
Definition: Offer_test.cpp:2146
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:94
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:101
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