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