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