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