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