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