rippled
Flow_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/paths/Flow.h>
21 #include <ripple/app/paths/impl/Steps.h>
22 #include <ripple/basics/contract.h>
23 #include <ripple/core/Config.h>
24 #include <ripple/ledger/ApplyViewImpl.h>
25 #include <ripple/ledger/PaymentSandbox.h>
26 #include <ripple/ledger/Sandbox.h>
27 #include <ripple/protocol/Feature.h>
28 #include <ripple/protocol/jss.h>
29 #include <test/jtx.h>
30 #include <test/jtx/PathSet.h>
31 
32 namespace ripple {
33 namespace test {
34 
35 bool
37  jtx::Env const& env,
38  jtx::Account const& src,
39  jtx::Account const& dst,
40  Currency const& cur)
41 {
42  if (auto sle = env.le(keylet::line(src, dst, cur)))
43  {
44  auto const flag =
45  (src.id() > dst.id()) ? lsfHighNoRipple : lsfLowNoRipple;
46  return sle->isFlag(flag);
47  }
48  Throw<std::runtime_error>("No line in getTrustFlag");
49  return false; // silence warning
50 }
51 
52 jtx::PrettyAmount
53 xrpMinusFee(jtx::Env const& env, std::int64_t xrpAmount)
54 {
55  using namespace jtx;
56  auto feeDrops = env.current()->fees().base;
57  return drops(dropsPerXRP * xrpAmount - feeDrops);
58 };
59 
60 struct Flow_test : public beast::unit_test::suite
61 {
62  void
64  {
65  testcase("Direct Step");
66 
67  using namespace jtx;
68  auto const alice = Account("alice");
69  auto const bob = Account("bob");
70  auto const carol = Account("carol");
71  auto const dan = Account("dan");
72  auto const erin = Account("erin");
73  auto const USDA = alice["USD"];
74  auto const USDB = bob["USD"];
75  auto const USDC = carol["USD"];
76  auto const USDD = dan["USD"];
77  auto const gw = Account("gw");
78  auto const USD = gw["USD"];
79  {
80  // Pay USD, trivial path
81  Env env(*this, features);
82 
83  env.fund(XRP(10000), alice, bob, gw);
84  env.trust(USD(1000), alice, bob);
85  env(pay(gw, alice, USD(100)));
86  env(pay(alice, bob, USD(10)), paths(USD));
87  env.require(balance(bob, USD(10)));
88  }
89  {
90  // XRP transfer
91  Env env(*this, features);
92 
93  env.fund(XRP(10000), alice, bob);
94  env(pay(alice, bob, XRP(100)));
95  env.require(balance(bob, XRP(10000 + 100)));
96  env.require(balance(alice, xrpMinusFee(env, 10000 - 100)));
97  }
98  {
99  // Partial payments
100  Env env(*this, features);
101 
102  env.fund(XRP(10000), alice, bob, gw);
103  env.trust(USD(1000), alice, bob);
104  env(pay(gw, alice, USD(100)));
105  env(pay(alice, bob, USD(110)), paths(USD), ter(tecPATH_PARTIAL));
106  env.require(balance(bob, USD(0)));
107  env(pay(alice, bob, USD(110)),
108  paths(USD),
110  env.require(balance(bob, USD(100)));
111  }
112  {
113  // Pay by rippling through accounts, use path finder
114  Env env(*this, features);
115 
116  env.fund(XRP(10000), alice, bob, carol, dan);
117  env.trust(USDA(10), bob);
118  env.trust(USDB(10), carol);
119  env.trust(USDC(10), dan);
120  env(pay(alice, dan, USDC(10)), paths(USDA));
121  env.require(
122  balance(bob, USDA(10)),
123  balance(carol, USDB(10)),
124  balance(dan, USDC(10)));
125  }
126  {
127  // Pay by rippling through accounts, specify path
128  // and charge a transfer fee
129  Env env(*this, features);
130 
131  env.fund(XRP(10000), alice, bob, carol, dan);
132  env.trust(USDA(10), bob);
133  env.trust(USDB(10), alice, carol);
134  env.trust(USDC(10), dan);
135  env(rate(bob, 1.1));
136 
137  // alice will redeem to bob; a transfer fee will be charged
138  env(pay(bob, alice, USDB(6)));
139  env(pay(alice, dan, USDC(5)),
140  path(bob, carol),
141  sendmax(USDA(6)),
143  env.require(balance(dan, USDC(5)));
144  env.require(balance(alice, USDB(0.5)));
145  }
146  {
147  // Pay by rippling through accounts, specify path and transfer fee
148  // Test that the transfer fee is not charged when alice issues
149  Env env(*this, features);
150 
151  env.fund(XRP(10000), alice, bob, carol, dan);
152  env.trust(USDA(10), bob);
153  env.trust(USDB(10), alice, carol);
154  env.trust(USDC(10), dan);
155  env(rate(bob, 1.1));
156 
157  env(pay(alice, dan, USDC(5)),
158  path(bob, carol),
159  sendmax(USDA(6)),
161  env.require(balance(dan, USDC(5)));
162  env.require(balance(bob, USDA(5)));
163  }
164  {
165  // test best quality path is taken
166  // Paths: A->B->D->E ; A->C->D->E
167  Env env(*this, features);
168 
169  env.fund(XRP(10000), alice, bob, carol, dan, erin);
170  env.trust(USDA(10), bob, carol);
171  env.trust(USDB(10), dan);
172  env.trust(USDC(10), alice, dan);
173  env.trust(USDD(20), erin);
174  env(rate(bob, 1));
175  env(rate(carol, 1.1));
176 
177  // Pay alice so she redeems to carol and a transfer fee is charged
178  env(pay(carol, alice, USDC(10)));
179  env(pay(alice, erin, USDD(5)),
180  path(carol, dan),
181  path(bob, dan),
183 
184  env.require(balance(erin, USDD(5)));
185  env.require(balance(dan, USDB(5)));
186  env.require(balance(dan, USDC(0)));
187  }
188  {
189  // Limit quality
190  Env env(*this, features);
191 
192  env.fund(XRP(10000), alice, bob, carol);
193  env.trust(USDA(10), bob);
194  env.trust(USDB(10), carol);
195 
196  env(pay(alice, carol, USDB(5)),
197  sendmax(USDA(4)),
199  ter(tecPATH_DRY));
200  env.require(balance(carol, USDB(0)));
201 
202  env(pay(alice, carol, USDB(5)),
203  sendmax(USDA(4)),
205  env.require(balance(carol, USDB(4)));
206  }
207  }
208 
209  void
211  {
212  testcase("Line Quality");
213 
214  using namespace jtx;
215  auto const alice = Account("alice");
216  auto const bob = Account("bob");
217  auto const carol = Account("carol");
218  auto const dan = Account("dan");
219  auto const USDA = alice["USD"];
220  auto const USDB = bob["USD"];
221  auto const USDC = carol["USD"];
222  auto const USDD = dan["USD"];
223 
224  // Dan -> Bob -> Alice -> Carol; vary bobDanQIn and bobAliceQOut
225  for (auto bobDanQIn : {80, 100, 120})
226  for (auto bobAliceQOut : {80, 100, 120})
227  {
228  Env env(*this, features);
229  env.fund(XRP(10000), alice, bob, carol, dan);
230  env(trust(bob, USDD(100)), qualityInPercent(bobDanQIn));
231  env(trust(bob, USDA(100)), qualityOutPercent(bobAliceQOut));
232  env(trust(carol, USDA(100)));
233 
234  env(pay(alice, bob, USDA(100)));
235  env.require(balance(bob, USDA(100)));
236  env(pay(dan, carol, USDA(10)),
237  path(bob),
238  sendmax(USDD(100)),
240  env.require(balance(bob, USDA(90)));
241  if (bobAliceQOut > bobDanQIn)
242  env.require(balance(
243  bob,
244  USDD(10.0 * double(bobAliceQOut) / double(bobDanQIn))));
245  else
246  env.require(balance(bob, USDD(10)));
247  env.require(balance(carol, USDA(10)));
248  }
249 
250  // bob -> alice -> carol; vary carolAliceQIn
251  for (auto carolAliceQIn : {80, 100, 120})
252  {
253  Env env(*this, features);
254  env.fund(XRP(10000), alice, bob, carol);
255  env(trust(bob, USDA(10)));
256  env(trust(carol, USDA(10)), qualityInPercent(carolAliceQIn));
257 
258  env(pay(alice, bob, USDA(10)));
259  env.require(balance(bob, USDA(10)));
260  env(pay(bob, carol, USDA(5)), sendmax(USDA(10)));
261  auto const effectiveQ =
262  carolAliceQIn > 100 ? 1.0 : carolAliceQIn / 100.0;
263  env.require(balance(bob, USDA(10.0 - 5.0 / effectiveQ)));
264  }
265 
266  // bob -> alice -> carol; bobAliceQOut varies.
267  for (auto bobAliceQOut : {80, 100, 120})
268  {
269  Env env(*this, features);
270  env.fund(XRP(10000), alice, bob, carol);
271  env(trust(bob, USDA(10)), qualityOutPercent(bobAliceQOut));
272  env(trust(carol, USDA(10)));
273 
274  env(pay(alice, bob, USDA(10)));
275  env.require(balance(bob, USDA(10)));
276  env(pay(bob, carol, USDA(5)), sendmax(USDA(5)));
277  env.require(balance(carol, USDA(5)));
278  env.require(balance(bob, USDA(10 - 5)));
279  }
280  }
281 
282  void
284  {
285  testcase("Book Step");
286 
287  using namespace jtx;
288 
289  auto const gw = Account("gateway");
290  auto const USD = gw["USD"];
291  auto const BTC = gw["BTC"];
292  auto const EUR = gw["EUR"];
293  Account const alice("alice");
294  Account const bob("bob");
295  Account const carol("carol");
296 
297  {
298  // simple IOU/IOU offer
299  Env env(*this, features);
300 
301  env.fund(XRP(10000), alice, bob, carol, gw);
302  env.trust(USD(1000), alice, bob, carol);
303  env.trust(BTC(1000), alice, bob, carol);
304 
305  env(pay(gw, alice, BTC(50)));
306  env(pay(gw, bob, USD(50)));
307 
308  env(offer(bob, BTC(50), USD(50)));
309 
310  env(pay(alice, carol, USD(50)), path(~USD), sendmax(BTC(50)));
311 
312  env.require(balance(alice, BTC(0)));
313  env.require(balance(bob, BTC(50)));
314  env.require(balance(bob, USD(0)));
315  env.require(balance(carol, USD(50)));
316  BEAST_EXPECT(!isOffer(env, bob, BTC(50), USD(50)));
317  }
318  {
319  // simple IOU/XRP XRP/IOU offer
320  Env env(*this, features);
321 
322  env.fund(XRP(10000), alice, bob, carol, gw);
323  env.trust(USD(1000), alice, bob, carol);
324  env.trust(BTC(1000), alice, bob, carol);
325 
326  env(pay(gw, alice, BTC(50)));
327  env(pay(gw, bob, USD(50)));
328 
329  env(offer(bob, BTC(50), XRP(50)));
330  env(offer(bob, XRP(50), USD(50)));
331 
332  env(pay(alice, carol, USD(50)), path(~XRP, ~USD), sendmax(BTC(50)));
333 
334  env.require(balance(alice, BTC(0)));
335  env.require(balance(bob, BTC(50)));
336  env.require(balance(bob, USD(0)));
337  env.require(balance(carol, USD(50)));
338  BEAST_EXPECT(!isOffer(env, bob, XRP(50), USD(50)));
339  BEAST_EXPECT(!isOffer(env, bob, BTC(50), XRP(50)));
340  }
341  {
342  // simple XRP -> USD through offer and sendmax
343  Env env(*this, features);
344 
345  env.fund(XRP(10000), alice, bob, carol, gw);
346  env.trust(USD(1000), alice, bob, carol);
347  env.trust(BTC(1000), alice, bob, carol);
348 
349  env(pay(gw, bob, USD(50)));
350 
351  env(offer(bob, XRP(50), USD(50)));
352 
353  env(pay(alice, carol, USD(50)), path(~USD), sendmax(XRP(50)));
354 
355  env.require(balance(alice, xrpMinusFee(env, 10000 - 50)));
356  env.require(balance(bob, xrpMinusFee(env, 10000 + 50)));
357  env.require(balance(bob, USD(0)));
358  env.require(balance(carol, USD(50)));
359  BEAST_EXPECT(!isOffer(env, bob, XRP(50), USD(50)));
360  }
361  {
362  // simple USD -> XRP through offer and sendmax
363  Env env(*this, features);
364 
365  env.fund(XRP(10000), alice, bob, carol, gw);
366  env.trust(USD(1000), alice, bob, carol);
367  env.trust(BTC(1000), alice, bob, carol);
368 
369  env(pay(gw, alice, USD(50)));
370 
371  env(offer(bob, USD(50), XRP(50)));
372 
373  env(pay(alice, carol, XRP(50)), path(~XRP), sendmax(USD(50)));
374 
375  env.require(balance(alice, USD(0)));
376  env.require(balance(bob, xrpMinusFee(env, 10000 - 50)));
377  env.require(balance(bob, USD(50)));
378  env.require(balance(carol, XRP(10000 + 50)));
379  BEAST_EXPECT(!isOffer(env, bob, USD(50), XRP(50)));
380  }
381  {
382  // test unfunded offers are removed when payment succeeds
383  Env env(*this, features);
384 
385  env.fund(XRP(10000), alice, bob, carol, gw);
386  env.trust(USD(1000), alice, bob, carol);
387  env.trust(BTC(1000), alice, bob, carol);
388  env.trust(EUR(1000), alice, bob, carol);
389 
390  env(pay(gw, alice, BTC(60)));
391  env(pay(gw, bob, USD(50)));
392  env(pay(gw, bob, EUR(50)));
393 
394  env(offer(bob, BTC(50), USD(50)));
395  env(offer(bob, BTC(60), EUR(50)));
396  env(offer(bob, EUR(50), USD(50)));
397 
398  // unfund offer
399  env(pay(bob, gw, EUR(50)));
400  BEAST_EXPECT(isOffer(env, bob, BTC(50), USD(50)));
401  BEAST_EXPECT(isOffer(env, bob, BTC(60), EUR(50)));
402  BEAST_EXPECT(isOffer(env, bob, EUR(50), USD(50)));
403 
404  env(pay(alice, carol, USD(50)),
405  path(~USD),
406  path(~EUR, ~USD),
407  sendmax(BTC(60)));
408 
409  env.require(balance(alice, BTC(10)));
410  env.require(balance(bob, BTC(50)));
411  env.require(balance(bob, USD(0)));
412  env.require(balance(bob, EUR(0)));
413  env.require(balance(carol, USD(50)));
414  // used in the payment
415  BEAST_EXPECT(!isOffer(env, bob, BTC(50), USD(50)));
416  // found unfunded
417  BEAST_EXPECT(!isOffer(env, bob, BTC(60), EUR(50)));
418  // unfunded, but should not yet be found unfunded
419  BEAST_EXPECT(isOffer(env, bob, EUR(50), USD(50)));
420  }
421  {
422  // test unfunded offers are returned when the payment fails.
423  // bob makes two offers: a funded 50 USD for 50 BTC and an unfunded
424  // 50 EUR for 60 BTC. alice pays carol 61 USD with 61 BTC. alice
425  // only has 60 BTC, so the payment will fail. The payment uses two
426  // paths: one through bob's funded offer and one through his
427  // unfunded offer. When the payment fails `flow` should return the
428  // unfunded offer. This test is intentionally similar to the one
429  // that removes unfunded offers when the payment succeeds.
430  Env env(*this, features);
431 
432  env.fund(XRP(10000), alice, bob, carol, gw);
433  env.trust(USD(1000), alice, bob, carol);
434  env.trust(BTC(1000), alice, bob, carol);
435  env.trust(EUR(1000), alice, bob, carol);
436 
437  env(pay(gw, alice, BTC(60)));
438  env(pay(gw, bob, USD(50)));
439  env(pay(gw, bob, EUR(50)));
440 
441  env(offer(bob, BTC(50), USD(50)));
442  env(offer(bob, BTC(60), EUR(50)));
443  env(offer(bob, EUR(50), USD(50)));
444 
445  // unfund offer
446  env(pay(bob, gw, EUR(50)));
447  BEAST_EXPECT(isOffer(env, bob, BTC(50), USD(50)));
448  BEAST_EXPECT(isOffer(env, bob, BTC(60), EUR(50)));
449 
450  auto flowJournal = env.app().logs().journal("Flow");
451  auto const flowResult = [&] {
452  STAmount deliver(USD(51));
453  STAmount smax(BTC(61));
454  PaymentSandbox sb(env.current().get(), tapNONE);
456  auto IPE = [](Issue const& iss) {
457  return STPathElement(
459  xrpAccount(),
460  iss.currency,
461  iss.account);
462  };
463  {
464  // BTC -> USD
465  STPath p1({IPE(USD.issue())});
466  paths.push_back(p1);
467  // BTC -> EUR -> USD
468  STPath p2({IPE(EUR.issue()), IPE(USD.issue())});
469  paths.push_back(p2);
470  }
471 
472  return flow(
473  sb,
474  deliver,
475  alice,
476  carol,
477  paths,
478  false,
479  false,
480  true,
481  false,
482  boost::none,
483  smax,
484  flowJournal);
485  }();
486 
487  BEAST_EXPECT(flowResult.removableOffers.size() == 1);
488  env.app().openLedger().modify(
489  [&](OpenView& view, beast::Journal j) {
490  if (flowResult.removableOffers.empty())
491  return false;
492  Sandbox sb(&view, tapNONE);
493  for (auto const& o : flowResult.removableOffers)
494  if (auto ok = sb.peek(keylet::offer(o)))
495  offerDelete(sb, ok, flowJournal);
496  sb.apply(view);
497  return true;
498  });
499 
500  // used in payment, but since payment failed should be untouched
501  BEAST_EXPECT(isOffer(env, bob, BTC(50), USD(50)));
502  // found unfunded
503  BEAST_EXPECT(!isOffer(env, bob, BTC(60), EUR(50)));
504  }
505  {
506  // Do not produce more in the forward pass than the reverse pass
507  // This test uses a path that whose reverse pass will compute a
508  // 0.5 USD input required for a 1 EUR output. It sets a sendmax of
509  // 0.4 USD, so the payment engine will need to do a forward pass.
510  // Without limits, the 0.4 USD would produce 1000 EUR in the forward
511  // pass. This test checks that the payment produces 1 EUR, as
512  // expected.
513 
514  Env env(*this, features);
515  env.fund(XRP(10000), alice, bob, carol, gw);
516  env.trust(USD(1000), alice, bob, carol);
517  env.trust(EUR(1000), alice, bob, carol);
518 
519  env(pay(gw, alice, USD(1000)));
520  env(pay(gw, bob, EUR(1000)));
521 
522  env(offer(bob, USD(1), drops(2)), txflags(tfPassive));
523  env(offer(bob, drops(1), EUR(1000)), txflags(tfPassive));
524 
525  env(pay(alice, carol, EUR(1)),
526  path(~XRP, ~EUR),
527  sendmax(USD(0.4)),
529 
530  env.require(balance(carol, EUR(1)));
531  env.require(balance(bob, USD(0.4)));
532  env.require(balance(bob, EUR(999)));
533  }
534  }
535 
536  void
538  {
539  testcase("Transfer Rate");
540 
541  using namespace jtx;
542 
543  auto const gw = Account("gateway");
544  auto const USD = gw["USD"];
545  auto const BTC = gw["BTC"];
546  auto const EUR = gw["EUR"];
547  Account const alice("alice");
548  Account const bob("bob");
549  Account const carol("carol");
550 
551  {
552  // Simple payment through a gateway with a
553  // transfer rate
554  Env env(*this, features);
555 
556  env.fund(XRP(10000), alice, bob, carol, gw);
557  env(rate(gw, 1.25));
558  env.trust(USD(1000), alice, bob, carol);
559  env(pay(gw, alice, USD(50)));
560  env.require(balance(alice, USD(50)));
561  env(pay(alice, bob, USD(40)), sendmax(USD(50)));
562  env.require(balance(bob, USD(40)), balance(alice, USD(0)));
563  }
564  {
565  // transfer rate is not charged when issuer is src or dst
566  Env env(*this, features);
567 
568  env.fund(XRP(10000), alice, bob, carol, gw);
569  env(rate(gw, 1.25));
570  env.trust(USD(1000), alice, bob, carol);
571  env(pay(gw, alice, USD(50)));
572  env.require(balance(alice, USD(50)));
573  env(pay(alice, gw, USD(40)), sendmax(USD(40)));
574  env.require(balance(alice, USD(10)));
575  }
576  {
577  // transfer fee on an offer
578  Env env(*this, features);
579 
580  env.fund(XRP(10000), alice, bob, carol, gw);
581  env(rate(gw, 1.25));
582  env.trust(USD(1000), alice, bob, carol);
583  env(pay(gw, bob, USD(65)));
584 
585  env(offer(bob, XRP(50), USD(50)));
586 
587  env(pay(alice, carol, USD(50)), path(~USD), sendmax(XRP(50)));
588  env.require(
589  balance(alice, xrpMinusFee(env, 10000 - 50)),
590  balance(bob, USD(2.5)), // owner pays transfer fee
591  balance(carol, USD(50)));
592  }
593 
594  {
595  // Transfer fee two consecutive offers
596  Env env(*this, features);
597 
598  env.fund(XRP(10000), alice, bob, carol, gw);
599  env(rate(gw, 1.25));
600  env.trust(USD(1000), alice, bob, carol);
601  env.trust(EUR(1000), alice, bob, carol);
602  env(pay(gw, bob, USD(50)));
603  env(pay(gw, bob, EUR(50)));
604 
605  env(offer(bob, XRP(50), USD(50)));
606  env(offer(bob, USD(50), EUR(50)));
607 
608  env(pay(alice, carol, EUR(40)), path(~USD, ~EUR), sendmax(XRP(40)));
609  env.require(
610  balance(alice, xrpMinusFee(env, 10000 - 40)),
611  balance(bob, USD(40)),
612  balance(bob, EUR(0)),
613  balance(carol, EUR(40)));
614  }
615 
616  {
617  // First pass through a strand redeems, second pass issues, no
618  // offers limiting step is not an endpoint
619  Env env(*this, features);
620  auto const USDA = alice["USD"];
621  auto const USDB = bob["USD"];
622 
623  env.fund(XRP(10000), alice, bob, carol, gw);
624  env(rate(gw, 1.25));
625  env.trust(USD(1000), alice, bob, carol);
626  env.trust(USDA(1000), bob);
627  env.trust(USDB(1000), gw);
628  env(pay(gw, bob, USD(50)));
629  // alice -> bob -> gw -> carol. $50 should have transfer fee; $10,
630  // no fee
631  env(pay(alice, carol, USD(50)), path(bob), sendmax(USDA(60)));
632  env.require(
633  balance(bob, USD(-10)),
634  balance(bob, USDA(60)),
635  balance(carol, USD(50)));
636  }
637  {
638  // First pass through a strand redeems, second pass issues, through
639  // an offer limiting step is not an endpoint
640  Env env(*this, features);
641  auto const USDA = alice["USD"];
642  auto const USDB = bob["USD"];
643  Account const dan("dan");
644 
645  env.fund(XRP(10000), alice, bob, carol, dan, gw);
646  env(rate(gw, 1.25));
647  env.trust(USD(1000), alice, bob, carol, dan);
648  env.trust(EUR(1000), carol, dan);
649  env.trust(USDA(1000), bob);
650  env.trust(USDB(1000), gw);
651  env(pay(gw, bob, USD(50)));
652  env(pay(gw, dan, EUR(100)));
653  env(offer(dan, USD(100), EUR(100)));
654  // alice -> bob -> gw -> carol. $50 should have transfer fee; $10,
655  // no fee
656  env(pay(alice, carol, EUR(50)),
657  path(bob, gw, ~EUR),
658  sendmax(USDA(60)),
660  env.require(
661  balance(bob, USD(-10)),
662  balance(bob, USDA(60)),
663  balance(dan, USD(50)),
664  balance(dan, EUR(37.5)),
665  balance(carol, EUR(50)));
666  }
667 
668  {
669  // Offer where the owner is also the issuer, owner pays fee
670  Env env(*this, features);
671 
672  env.fund(XRP(10000), alice, bob, gw);
673  env(rate(gw, 1.25));
674  env.trust(USD(1000), alice, bob);
675  env(offer(gw, XRP(100), USD(100)));
676  env(pay(alice, bob, USD(100)), sendmax(XRP(100)));
677  env.require(
678  balance(alice, xrpMinusFee(env, 10000 - 100)),
679  balance(bob, USD(100)));
680  }
681  if (!features[featureOwnerPaysFee])
682  {
683  // Offer where the owner is also the issuer, sender pays fee
684  Env env(*this, features);
685 
686  env.fund(XRP(10000), alice, bob, gw);
687  env(rate(gw, 1.25));
688  env.trust(USD(1000), alice, bob);
689  env(offer(gw, XRP(125), USD(125)));
690  env(pay(alice, bob, USD(100)), sendmax(XRP(200)));
691  env.require(
692  balance(alice, xrpMinusFee(env, 10000 - 125)),
693  balance(bob, USD(100)));
694  }
695  }
696 
697  void
699  {
700  testcase("falseDryChanges");
701 
702  using namespace jtx;
703 
704  auto const gw = Account("gateway");
705  auto const USD = gw["USD"];
706  auto const EUR = gw["EUR"];
707  Account const alice("alice");
708  Account const bob("bob");
709  Account const carol("carol");
710 
711  Env env(*this, features);
712 
713  env.fund(XRP(10000), alice, carol, gw);
714  env.fund(reserve(env, 5), bob);
715  env.trust(USD(1000), alice, bob, carol);
716  env.trust(EUR(1000), alice, bob, carol);
717 
718  env(pay(gw, alice, EUR(50)));
719  env(pay(gw, bob, USD(50)));
720 
721  // Bob has _just_ slightly less than 50 xrp available
722  // If his owner count changes, he will have more liquidity.
723  // This is one error case to test (when Flow is used).
724  // Computing the incoming xrp to the XRP/USD offer will require two
725  // recursive calls to the EUR/XRP offer. The second call will return
726  // tecPATH_DRY, but the entire path should not be marked as dry. This
727  // is the second error case to test (when flowV1 is used).
728  env(offer(bob, EUR(50), XRP(50)));
729  env(offer(bob, XRP(50), USD(50)));
730 
731  env(pay(alice, carol, USD(1000000)),
732  path(~XRP, ~USD),
733  sendmax(EUR(500)),
735 
736  auto const carolUSD = env.balance(carol, USD).value();
737  BEAST_EXPECT(carolUSD > USD(0) && carolUSD < USD(50));
738  }
739 
740  void
742  {
743  // Single path with two offers and limit quality. The quality limit is
744  // such that the first offer should be taken but the second should not.
745  // The total amount delivered should be the sum of the two offers and
746  // sendMax should be more than the first offer.
747  testcase("limitQuality");
748  using namespace jtx;
749 
750  auto const gw = Account("gateway");
751  auto const USD = gw["USD"];
752  Account const alice("alice");
753  Account const bob("bob");
754  Account const carol("carol");
755 
756  {
757  Env env(*this, supported_amendments());
758 
759  env.fund(XRP(10000), alice, bob, carol, gw);
760 
761  env.trust(USD(100), alice, bob, carol);
762  env(pay(gw, bob, USD(100)));
763  env(offer(bob, XRP(50), USD(50)));
764  env(offer(bob, XRP(100), USD(50)));
765 
766  env(pay(alice, carol, USD(100)),
767  path(~USD),
768  sendmax(XRP(100)),
770 
771  env.require(balance(carol, USD(50)));
772  }
773  }
774 
775  // Helper function that returns the reserve on an account based on
776  // the passed in number of owners.
777  static XRPAmount
779  {
780  return env.current()->fees().accountReserve(count);
781  }
782 
783  // Helper function that returns the Offers on an account.
786  {
788  forEachItem(
789  *env.current(),
790  account,
791  [&result](std::shared_ptr<SLE const> const& sle) {
792  if (sle->getType() == ltOFFER)
793  result.push_back(sle);
794  });
795  return result;
796  }
797 
798  void
800  {
801  testcase("Self-payment 1");
802 
803  // In this test case the new flow code mis-computes the amount
804  // of money to move. Fortunately the new code's re-execute
805  // check catches the problem and throws out the transaction.
806  //
807  // The old payment code handles the payment correctly.
808  using namespace jtx;
809 
810  auto const gw1 = Account("gw1");
811  auto const gw2 = Account("gw2");
812  auto const alice = Account("alice");
813  auto const USD = gw1["USD"];
814  auto const EUR = gw2["EUR"];
815 
816  Env env(*this, features);
817 
818  env.fund(XRP(1000000), gw1, gw2);
819  env.close();
820 
821  // The fee that's charged for transactions.
822  auto const f = env.current()->fees().base;
823 
824  env.fund(reserve(env, 3) + f * 4, alice);
825  env.close();
826 
827  env(trust(alice, USD(2000)));
828  env(trust(alice, EUR(2000)));
829  env.close();
830 
831  env(pay(gw1, alice, USD(1)));
832  env(pay(gw2, alice, EUR(1000)));
833  env.close();
834 
835  env(offer(alice, USD(500), EUR(600)));
836  env.close();
837 
838  env.require(owners(alice, 3));
839  env.require(balance(alice, USD(1)));
840  env.require(balance(alice, EUR(1000)));
841 
842  auto aliceOffers = offersOnAccount(env, alice);
843  BEAST_EXPECT(aliceOffers.size() == 1);
844  for (auto const& offerPtr : aliceOffers)
845  {
846  auto const offer = *offerPtr;
847  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
848  BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
849  BEAST_EXPECT(offer[sfTakerPays] == USD(500));
850  }
851 
852  env(pay(alice, alice, EUR(600)),
853  sendmax(USD(500)),
855  env.close();
856 
857  env.require(owners(alice, 3));
858  env.require(balance(alice, USD(1)));
859  env.require(balance(alice, EUR(1000)));
860  aliceOffers = offersOnAccount(env, alice);
861  BEAST_EXPECT(aliceOffers.size() == 1);
862  for (auto const& offerPtr : aliceOffers)
863  {
864  auto const offer = *offerPtr;
865  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
866  BEAST_EXPECT(offer[sfTakerGets] == EUR(598.8));
867  BEAST_EXPECT(offer[sfTakerPays] == USD(499));
868  }
869  }
870 
871  void
873  {
874  testcase("Self-payment 2");
875 
876  // In this case the difference between the old payment code and
877  // the new is the values left behind in the offer. Not saying either
878  // ios ring, they are just different.
879  using namespace jtx;
880 
881  auto const gw1 = Account("gw1");
882  auto const gw2 = Account("gw2");
883  auto const alice = Account("alice");
884  auto const USD = gw1["USD"];
885  auto const EUR = gw2["EUR"];
886 
887  Env env(*this, features);
888 
889  env.fund(XRP(1000000), gw1, gw2);
890  env.close();
891 
892  // The fee that's charged for transactions.
893  auto const f = env.current()->fees().base;
894 
895  env.fund(reserve(env, 3) + f * 4, alice);
896  env.close();
897 
898  env(trust(alice, USD(506)));
899  env(trust(alice, EUR(606)));
900  env.close();
901 
902  env(pay(gw1, alice, USD(500)));
903  env(pay(gw2, alice, EUR(600)));
904  env.close();
905 
906  env(offer(alice, USD(500), EUR(600)));
907  env.close();
908 
909  env.require(owners(alice, 3));
910  env.require(balance(alice, USD(500)));
911  env.require(balance(alice, EUR(600)));
912 
913  auto aliceOffers = offersOnAccount(env, alice);
914  BEAST_EXPECT(aliceOffers.size() == 1);
915  for (auto const& offerPtr : aliceOffers)
916  {
917  auto const offer = *offerPtr;
918  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
919  BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
920  BEAST_EXPECT(offer[sfTakerPays] == USD(500));
921  }
922 
923  env(pay(alice, alice, EUR(60)),
924  sendmax(USD(50)),
926  env.close();
927 
928  env.require(owners(alice, 3));
929  env.require(balance(alice, USD(500)));
930  env.require(balance(alice, EUR(600)));
931  aliceOffers = offersOnAccount(env, alice);
932  BEAST_EXPECT(aliceOffers.size() == 1);
933  for (auto const& offerPtr : aliceOffers)
934  {
935  auto const offer = *offerPtr;
936  BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
937  BEAST_EXPECT(offer[sfTakerGets] == EUR(594));
938  BEAST_EXPECT(offer[sfTakerPays] == USD(495));
939  }
940  }
941  void
942  testSelfFundedXRPEndpoint(bool consumeOffer, FeatureBitset features)
943  {
944  // Test that the deferred credit table is not bypassed for
945  // XRPEndpointSteps. If the account in the first step is sending XRP and
946  // that account also owns an offer that receives XRP, it should not be
947  // possible for that step to use the XRP received in the offer as part
948  // of the payment.
949  testcase("Self funded XRPEndpoint");
950 
951  using namespace jtx;
952 
953  Env env(*this, features);
954 
955  auto const alice = Account("alice");
956  auto const gw = Account("gw");
957  auto const USD = gw["USD"];
958 
959  env.fund(XRP(10000), alice, gw);
960  env(trust(alice, USD(20)));
961  env(pay(gw, alice, USD(10)));
962  env(offer(alice, XRP(50000), USD(10)));
963 
964  // Consuming the offer changes the owner count, which could also cause
965  // liquidity to decrease in the forward pass
966  auto const toSend = consumeOffer ? USD(10) : USD(9);
967  env(pay(alice, alice, toSend),
968  path(~USD),
969  sendmax(XRP(20000)),
971  }
972 
973  void
975  {
976  testcase("Unfunded Offer");
977 
978  using namespace jtx;
979  {
980  // Test reverse
981  Env env(*this, features);
982 
983  auto const alice = Account("alice");
984  auto const bob = Account("bob");
985  auto const gw = Account("gw");
986  auto const USD = gw["USD"];
987 
988  env.fund(XRP(100000), alice, bob, gw);
989  env(trust(bob, USD(20)));
990 
991  STAmount tinyAmt1{
992  USD.issue(),
993  9000000000000000ll,
994  -17,
995  false,
996  false,
998  STAmount tinyAmt3{
999  USD.issue(),
1000  9000000000000003ll,
1001  -17,
1002  false,
1003  false,
1005 
1006  env(offer(gw, drops(9000000000), tinyAmt3));
1007  env(pay(alice, bob, tinyAmt1),
1008  path(~USD),
1009  sendmax(drops(9000000000)),
1011 
1012  BEAST_EXPECT(!isOffer(env, gw, XRP(0), USD(0)));
1013  }
1014  {
1015  // Test forward
1016  Env env(*this, features);
1017 
1018  auto const alice = Account("alice");
1019  auto const bob = Account("bob");
1020  auto const gw = Account("gw");
1021  auto const USD = gw["USD"];
1022 
1023  env.fund(XRP(100000), alice, bob, gw);
1024  env(trust(alice, USD(20)));
1025 
1026  STAmount tinyAmt1{
1027  USD.issue(),
1028  9000000000000000ll,
1029  -17,
1030  false,
1031  false,
1033  STAmount tinyAmt3{
1034  USD.issue(),
1035  9000000000000003ll,
1036  -17,
1037  false,
1038  false,
1040 
1041  env(pay(gw, alice, tinyAmt1));
1042 
1043  env(offer(gw, tinyAmt3, drops(9000000000)));
1044  env(pay(alice, bob, drops(9000000000)),
1045  path(~XRP),
1046  sendmax(USD(1)),
1048 
1049  BEAST_EXPECT(!isOffer(env, gw, USD(0), XRP(0)));
1050  }
1051  }
1052 
1053  void
1055  {
1056  testcase("ReexecuteDirectStep");
1057 
1058  using namespace jtx;
1059  Env env(*this, features);
1060 
1061  auto const alice = Account("alice");
1062  auto const bob = Account("bob");
1063  auto const gw = Account("gw");
1064  auto const USD = gw["USD"];
1065  auto const usdC = USD.currency;
1066 
1067  env.fund(XRP(10000), alice, bob, gw);
1068  env.close();
1069  env(trust(alice, USD(100)));
1070  env.close();
1071 
1072  BEAST_EXPECT(!getNoRippleFlag(env, gw, alice, usdC));
1073 
1074  env(pay(
1075  gw,
1076  alice,
1077  // 12.55....
1078  STAmount{
1079  USD.issue(), std::uint64_t(1255555555555555ull), -14, false}));
1080 
1081  env(offer(
1082  gw,
1083  // 5.0...
1084  STAmount{
1085  USD.issue(), std::uint64_t(5000000000000000ull), -15, false},
1086  XRP(1000)));
1087 
1088  env(offer(
1089  gw,
1090  // .555...
1091  STAmount{
1092  USD.issue(), std::uint64_t(5555555555555555ull), -16, false},
1093  XRP(10)));
1094 
1095  env(offer(
1096  gw,
1097  // 4.44....
1098  STAmount{
1099  USD.issue(), std::uint64_t(4444444444444444ull), -15, false},
1100  XRP(.1)));
1101 
1102  env(offer(
1103  alice,
1104  // 17
1105  STAmount{
1106  USD.issue(), std::uint64_t(1700000000000000ull), -14, false},
1107  XRP(.001)));
1108 
1109  env(pay(alice, bob, XRP(10000)),
1110  path(~XRP),
1111  sendmax(USD(100)),
1113  }
1114 
1115  void
1117  {
1118  testcase("ripd1443");
1119 
1120  using namespace jtx;
1121  Env env(*this);
1122  auto const alice = Account("alice");
1123  auto const bob = Account("bob");
1124  auto const carol = Account("carol");
1125  auto const gw = Account("gw");
1126 
1127  env.fund(XRP(100000000), alice, noripple(bob), carol, gw);
1128  env.trust(gw["USD"](10000), alice, carol);
1129  env(trust(bob, gw["USD"](10000), tfSetNoRipple));
1130  env.trust(gw["USD"](10000), bob);
1131  env.close();
1132 
1133  // set no ripple between bob and the gateway
1134 
1135  env(pay(gw, alice, gw["USD"](1000)));
1136  env.close();
1137 
1138  env(offer(alice, bob["USD"](1000), XRP(1)));
1139  env.close();
1140 
1141  env(pay(alice, alice, XRP(1)),
1142  path(gw, bob, ~XRP),
1143  sendmax(gw["USD"](1000)),
1145  ter(tecPATH_DRY));
1146  env.close();
1147 
1148  env.trust(bob["USD"](10000), alice);
1149  env(pay(bob, alice, bob["USD"](1000)));
1150 
1151  env(offer(alice, XRP(1000), bob["USD"](1000)));
1152  env.close();
1153 
1154  env(pay(carol, carol, gw["USD"](1000)),
1155  path(~bob["USD"], gw),
1156  sendmax(XRP(100000)),
1158  ter(tecPATH_DRY));
1159  env.close();
1160 
1161  pass();
1162  }
1163 
1164  void
1166  {
1167  testcase("ripd1449");
1168 
1169  using namespace jtx;
1170  Env env(*this);
1171 
1172  // pay alice -> xrp -> USD/bob -> bob -> gw -> alice
1173  // set no ripple on bob's side of the bob/gw trust line
1174  // carol has the bob/USD and makes an offer, bob has USD/gw
1175 
1176  auto const alice = Account("alice");
1177  auto const bob = Account("bob");
1178  auto const carol = Account("carol");
1179  auto const gw = Account("gw");
1180  auto const USD = gw["USD"];
1181 
1182  env.fund(XRP(100000000), alice, bob, carol, gw);
1183  env.close();
1184  env.trust(USD(10000), alice, carol);
1185  env(trust(bob, USD(10000), tfSetNoRipple));
1186  env.trust(USD(10000), bob);
1187  env.trust(bob["USD"](10000), carol);
1188  env.close();
1189 
1190  env(pay(bob, carol, bob["USD"](1000)));
1191  env(pay(gw, bob, USD(1000)));
1192  env.close();
1193 
1194  env(offer(carol, XRP(1), bob["USD"](1000)));
1195  env.close();
1196 
1197  env(pay(alice, alice, USD(1000)),
1198  path(~bob["USD"], bob, gw),
1199  sendmax(XRP(1)),
1201  ter(tecPATH_DRY));
1202  env.close();
1203  }
1204 
1205  void
1207  {
1208  // The new payment code used to assert if an offer was made for more
1209  // XRP than the offering account held. This unit test reproduces
1210  // that failing case.
1211  testcase("Self crossing low quality offer");
1212 
1213  using namespace jtx;
1214 
1215  Env env(*this, features);
1216 
1217  auto const ann = Account("ann");
1218  auto const gw = Account("gateway");
1219  auto const CTB = gw["CTB"];
1220 
1221  auto const fee = env.current()->fees().base;
1222  env.fund(reserve(env, 2) + drops(9999640) + fee, ann);
1223  env.fund(reserve(env, 2) + fee * 4, gw);
1224  env.close();
1225 
1226  env(rate(gw, 1.002));
1227  env(trust(ann, CTB(10)));
1228  env.close();
1229 
1230  env(pay(gw, ann, CTB(2.856)));
1231  env.close();
1232 
1233  env(offer(ann, drops(365611702030), CTB(5.713)));
1234  env.close();
1235 
1236  // This payment caused the assert.
1237  env(pay(ann, ann, CTB(0.687)),
1238  sendmax(drops(20000000000)),
1240  }
1241 
1242  void
1244  {
1245  testcase("Empty Strand");
1246  using namespace jtx;
1247 
1248  auto const alice = Account("alice");
1249 
1250  Env env(*this, features);
1251 
1252  env.fund(XRP(10000), alice);
1253 
1254  env(pay(alice, alice, alice["USD"](100)),
1255  path(~alice["USD"]),
1256  ter(temBAD_PATH));
1257  }
1258 
1259  void
1261  {
1262  testcase("Circular XRP");
1263 
1264  using namespace jtx;
1265  auto const alice = Account("alice");
1266  auto const bob = Account("bob");
1267  auto const gw = Account("gw");
1268  auto const USD = gw["USD"];
1269  auto const EUR = gw["EUR"];
1270 
1271  for (auto const withFix : {true, false})
1272  {
1273  auto const feats = [&withFix]() -> FeatureBitset {
1274  if (withFix)
1275  return supported_amendments();
1277  }();
1278  {
1279  // Payment path starting with XRP
1280  Env env(*this, feats);
1281  env.fund(XRP(10000), alice, bob, gw);
1282  env.trust(USD(1000), alice, bob);
1283  env.trust(EUR(1000), alice, bob);
1284  env(pay(gw, alice, USD(100)));
1285  env(pay(gw, alice, EUR(100)));
1286  env.close();
1287 
1288  env(offer(alice, XRP(100), USD(100)), txflags(tfPassive));
1289  env(offer(alice, USD(100), XRP(100)), txflags(tfPassive));
1290  env(offer(alice, XRP(100), EUR(100)), txflags(tfPassive));
1291  env.close();
1292 
1293  TER const expectedTer =
1294  withFix ? TER{temBAD_PATH_LOOP} : TER{tesSUCCESS};
1295  env(pay(alice, bob, EUR(1)),
1296  path(~USD, ~XRP, ~EUR),
1297  sendmax(XRP(1)),
1299  ter(expectedTer));
1300  }
1301  pass();
1302  }
1303  {
1304  // Payment path ending with XRP
1305  Env env(*this);
1306  env.fund(XRP(10000), alice, bob, gw);
1307  env.trust(USD(1000), alice, bob);
1308  env.trust(EUR(1000), alice, bob);
1309  env(pay(gw, alice, USD(100)));
1310  env(pay(gw, alice, EUR(100)));
1311  env.close();
1312 
1313  env(offer(alice, XRP(100), USD(100)), txflags(tfPassive));
1314  env(offer(alice, EUR(100), XRP(100)), txflags(tfPassive));
1315  env.close();
1316  // EUR -> //XRP -> //USD ->XRP
1317  env(pay(alice, bob, XRP(1)),
1318  path(~XRP, ~USD, ~XRP),
1319  sendmax(EUR(1)),
1322  }
1323  {
1324  // Payment where loop is formed in the middle of the path, not on an
1325  // endpoint
1326  auto const JPY = gw["JPY"];
1327  Env env(*this);
1328  env.fund(XRP(10000), alice, bob, gw);
1329  env.close();
1330  env.trust(USD(1000), alice, bob);
1331  env.trust(EUR(1000), alice, bob);
1332  env.trust(JPY(1000), alice, bob);
1333  env.close();
1334  env(pay(gw, alice, USD(100)));
1335  env(pay(gw, alice, EUR(100)));
1336  env(pay(gw, alice, JPY(100)));
1337  env.close();
1338 
1339  env(offer(alice, USD(100), XRP(100)), txflags(tfPassive));
1340  env(offer(alice, XRP(100), EUR(100)), txflags(tfPassive));
1341  env(offer(alice, EUR(100), XRP(100)), txflags(tfPassive));
1342  env(offer(alice, XRP(100), JPY(100)), txflags(tfPassive));
1343  env.close();
1344 
1345  env(pay(alice, bob, JPY(1)),
1346  path(~XRP, ~EUR, ~XRP, ~JPY),
1347  sendmax(USD(1)),
1350  }
1351  }
1352 
1353  void
1355  {
1356  testcase("Payment with ticket");
1357  using namespace jtx;
1358 
1359  auto const alice = Account("alice");
1360  auto const bob = Account("bob");
1361 
1362  Env env(*this, features);
1363  BEAST_EXPECT(features[featureTicketBatch]);
1364 
1365  env.fund(XRP(10000), alice);
1366 
1367  // alice creates a ticket for the payment.
1368  std::uint32_t const ticketSeq{env.seq(alice) + 1};
1369  env(ticket::create(alice, 1));
1370 
1371  // Make a payment using the ticket.
1372  env(pay(alice, bob, XRP(1000)), ticket::use(ticketSeq));
1373  env.close();
1374  env.require(balance(bob, XRP(1000)));
1375  env.require(balance(alice, XRP(9000) - drops(20)));
1376  }
1377 
1378  void
1380  {
1381  using namespace jtx;
1382  FeatureBitset const ownerPaysFee{featureOwnerPaysFee};
1383 
1384  testLineQuality(features);
1385  testFalseDry(features);
1386  testDirectStep(features);
1387  testBookStep(features);
1388  testDirectStep(features | ownerPaysFee);
1389  testBookStep(features | ownerPaysFee);
1390  testTransferRate(features | ownerPaysFee);
1391  testSelfPayment1(features);
1392  testSelfPayment2(features);
1393  testSelfFundedXRPEndpoint(false, features);
1394  testSelfFundedXRPEndpoint(true, features);
1395  testUnfundedOffer(features);
1396  testReexecuteDirectStep(features);
1397  testSelfPayLowQualityOffer(features);
1398  testTicketPay(features);
1399  }
1400 
1401  void
1402  run() override
1403  {
1404  testLimitQuality();
1405  testXRPPathLoop();
1406  testRIPD1443();
1407  testRIPD1449();
1408 
1409  using namespace jtx;
1410  auto const sa = supported_amendments() | featureTicketBatch;
1412  testWithFeats(sa);
1413  testEmptyStrand(sa);
1414  }
1415 };
1416 
1418 {
1419  void
1420  run() override
1421  {
1422  using namespace jtx;
1423  auto const all = supported_amendments() | featureTicketBatch;
1424  FeatureBitset const flowCross{featureFlowCross};
1425  FeatureBitset const f1513{fix1513};
1426 
1427  testWithFeats(all - flowCross - f1513);
1428  testWithFeats(all - flowCross);
1429  testWithFeats(all - f1513);
1430  testWithFeats(all);
1431 
1432  testEmptyStrand(all - f1513);
1434  }
1435 };
1436 
1437 BEAST_DEFINE_TESTSUITE_PRIO(Flow, app, ripple, 2);
1438 BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Flow_manual, app, ripple, 4);
1439 
1440 } // namespace test
1441 } // namespace ripple
ripple::test::BEAST_DEFINE_TESTSUITE_PRIO
BEAST_DEFINE_TESTSUITE_PRIO(Flow, app, ripple, 2)
ripple::tfNoRippleDirect
const std::uint32_t tfNoRippleDirect
Definition: TxFlags.h:84
ripple::test::Flow_test::testLineQuality
void testLineQuality(FeatureBitset features)
Definition: Flow_test.cpp:210
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
ripple::test::jtx::dropsPerXRP
constexpr XRPAmount dropsPerXRP
Definition: amount.h:67
ripple::Issue
A currency issued by an account.
Definition: Issue.h:34
std::shared_ptr
STL class.
ripple::test::Flow_manual_test::run
void run() override
Definition: Flow_test.cpp:1420
ripple::tfPartialPayment
const std::uint32_t tfPartialPayment
Definition: TxFlags.h:85
ripple::test::jtx::drops
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Definition: amount.h:241
ripple::test::Flow_test::testReexecuteDirectStep
void testReexecuteDirectStep(FeatureBitset features)
Definition: Flow_test.cpp:1054
ripple::sfLedgerEntryType
const SF_U16 sfLedgerEntryType(access, STI_UINT16, 1, "LedgerEntryType", SField::sMD_Never)
Definition: SField.h:346
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::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:112
ripple::test::jtx::owners
Match the number of items in the account's owner directory.
Definition: owners.h:69
ripple::test::jtx::Env::require
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:466
ripple::TxSearched::all
@ all
ripple::test::jtx::balance
A balance matches.
Definition: balance.h:38
ripple::OpenView
Writable ledger view that accumulates state and tx changes.
Definition: OpenView.h:52
ripple::lsfLowNoRipple
@ lsfLowNoRipple
Definition: LedgerFormats.h:125
std::vector
STL class.
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::test::Flow_test::testFalseDry
void testFalseDry(FeatureBitset features)
Definition: Flow_test.cpp:698
ripple::tfPassive
const std::uint32_t tfPassive
Definition: TxFlags.h:76
ripple::test::Flow_test::testSelfFundedXRPEndpoint
void testSelfFundedXRPEndpoint(bool consumeOffer, FeatureBitset features)
Definition: Flow_test.cpp:942
ripple::keylet::offer
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:218
ripple::fix1781
const uint256 fix1781
Definition: Feature.cpp:185
ripple::sfTakerPays
const SF_Amount sfTakerPays(access, STI_AMOUNT, 4, "TakerPays")
Definition: SField.h:444
ripple::test::Flow_test::testWithFeats
void testWithFeats(FeatureBitset features)
Definition: Flow_test.cpp:1379
ripple::test::jtx::Env::balance
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition: Env.cpp:174
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:240
ripple::test::Flow_test::testRIPD1443
void testRIPD1443()
Definition: Flow_test.cpp:1116
ripple::Application::openLedger
virtual OpenLedger & openLedger()=0
ripple::test::Flow_test::testEmptyStrand
void testEmptyStrand(FeatureBitset features)
Definition: Flow_test.cpp:1243
ripple::tapNONE
@ tapNONE
Definition: ApplyView.h:31
ripple::temBAD_PATH
@ temBAD_PATH
Definition: TER.h:91
ripple::test::Flow_test::testUnfundedOffer
void testUnfundedOffer(FeatureBitset features)
Definition: Flow_test.cpp:974
ripple::test::jtx::qualityInPercent
Sets the QualityIn on a trust JTx.
Definition: quality.h:45
ripple::test::Flow_test::run
void run() override
Definition: Flow_test.cpp:1402
ripple::STPathElement::typeCurrency
@ typeCurrency
Definition: STPathSet.h:40
ripple::STPathSet
Definition: STPathSet.h:309
ripple::test::jtx::Env::trust
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:250
ripple::STPathElement::typeIssuer
@ typeIssuer
Definition: STPathSet.h:41
ripple::test::jtx::Account::id
AccountID id() const
Returns the Account ID.
Definition: Account.h:102
ripple::forEachItem
void forEachItem(ReadView const &view, AccountID const &id, std::function< void(std::shared_ptr< SLE const > const &)> f)
Iterate all items in an account's owner directory.
Definition: View.cpp:251
ripple::test::Flow_test::testSelfPayment1
void testSelfPayment1(FeatureBitset features)
Definition: Flow_test.cpp:799
ripple::base_uint< 160, detail::CurrencyTag >
ripple::test::jtx::ticket::use
Set a ticket sequence on a JTx.
Definition: ticket.h:47
ripple::test::BEAST_DEFINE_TESTSUITE_MANUAL_PRIO
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(CrossingLimits, tx, ripple, 10)
ripple::offerDelete
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition: View.cpp:896
ripple::fix1513
const uint256 fix1513
Definition: Feature.cpp:168
ripple::TERSubset< CanCvtToTER >
ripple::test::jtx::sendmax
Sets the SendMax on a JTx.
Definition: sendmax.h:31
ripple::Sandbox
Discardable, editable view to a ledger.
Definition: Sandbox.h:34
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::getNoRippleFlag
bool getNoRippleFlag(jtx::Env const &env, jtx::Account const &src, jtx::Account const &dst, Currency const &cur)
Definition: Flow_test.cpp:36
ripple::STAmount
Definition: STAmount.h:42
ripple::xrpAccount
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:130
ripple::test::isOffer
bool isOffer(jtx::Env &env, jtx::Account const &account, STAmount const &takerPays, STAmount const &takerGets)
An offer exists.
Definition: PathSet.h:33
ripple::test::Flow_manual_test
Definition: Flow_test.cpp:1417
ripple::Application::logs
virtual Logs & logs()=0
ripple::test::jtx::path
Add a path.
Definition: paths.h:55
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::test::jtx::supported_amendments
FeatureBitset supported_amendments()
Definition: Env.h:70
std::int64_t
ripple::tecPATH_PARTIAL
@ tecPATH_PARTIAL
Definition: TER.h:243
ripple::keylet::line
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition: Indexes.cpp:189
ripple::test::jtx::Env::seq
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition: Env.cpp:198
ripple::test::Flow_test::testTransferRate
void testTransferRate(FeatureBitset features)
Definition: Flow_test.cpp:537
ripple::test::Flow_test::testRIPD1449
void testRIPD1449()
Definition: Flow_test.cpp:1165
ripple::test::jtx::qualityOutPercent
Sets the QualityOut on a trust JTx as a percentage.
Definition: quality.h:73
ripple::test::jtx::fee
Set the fee on a JTx.
Definition: fee.h:34
ripple::lsfHighNoRipple
@ lsfHighNoRipple
Definition: LedgerFormats.h:126
ripple::test::Flow_test::reserve
static XRPAmount reserve(jtx::Env &env, std::uint32_t count)
Definition: Flow_test.cpp:778
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::Flow_test::offersOnAccount
static std::vector< std::shared_ptr< SLE const > > offersOnAccount(jtx::Env &env, jtx::Account account)
Definition: Flow_test.cpp:785
ripple::tfSetNoRipple
const std::uint32_t tfSetNoRipple
Definition: TxFlags.h:92
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::STAmount::unchecked
Definition: STAmount.h:77
ripple::Logs::journal
beast::Journal journal(std::string const &name)
Definition: Log.cpp:144
ripple::test::jtx::pay
Json::Value pay(Account const &account, Account const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
ripple::STPathElement
Definition: STPathSet.h:33
ripple::test::jtx::Env::close
bool close(NetClock::time_point closeTime, boost::optional< std::chrono::milliseconds > consensusDelay=boost::none)
Close and advance the ledger.
Definition: Env.cpp:121
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:197
ripple::test::Flow_test::testDirectStep
void testDirectStep(FeatureBitset features)
Definition: Flow_test.cpp:63
ripple::test::Flow_test::testLimitQuality
void testLimitQuality()
Definition: Flow_test.cpp:741
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:219
ripple::test::jtx::Env::le
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:207
ripple::tfLimitQuality
const std::uint32_t tfLimitQuality
Definition: TxFlags.h:86
ripple::test::IPE
auto IPE(Issue const &iss)
Definition: Path_test.cpp:161
ripple::test::Flow_test::testTicketPay
void testTicketPay(FeatureBitset features)
Definition: Flow_test.cpp:1354
ripple::FeatureBitset
Definition: Feature.h:156
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:255
ripple::sfTakerGets
const SF_Amount sfTakerGets(access, STI_AMOUNT, 5, "TakerGets")
Definition: SField.h:445
ripple::featureTicketBatch
const uint256 featureTicketBatch
Definition: Feature.cpp:189
ripple::flow
path::RippleCalc::Output flow(PaymentSandbox &view, STAmount const &deliver, AccountID const &src, AccountID const &dst, STPathSet const &paths, bool defaultPaths, bool partialPayment, bool ownerPaysTransferFee, bool offerCrossing, boost::optional< Quality > const &limitQuality, boost::optional< STAmount > const &sendMax, beast::Journal j, path::detail::FlowDebugInfo *flowDebugInfo=nullptr)
Make a payment from the src account to the dst account.
ripple::test::jtx::PrettyAmount::value
STAmount const & value() const
Definition: amount.h:124
ripple::test::jtx::Account
Immutable cryptographic account descriptor.
Definition: Account.h:37
ripple::temBAD_PATH_LOOP
@ temBAD_PATH_LOOP
Definition: TER.h:92
ripple::featureOwnerPaysFee
const uint256 featureOwnerPaysFee
Definition: Feature.cpp:163
ripple::ltOFFER
@ ltOFFER
Definition: LedgerFormats.h:72
ripple::test::Flow_test::testXRPPathLoop
void testXRPPathLoop()
Definition: Flow_test.cpp:1260
ripple::featureFlowCross
const uint256 featureFlowCross
Definition: Feature.cpp:166
ripple::STPath
Definition: STPathSet.h:212
ripple::test::Flow_test::testBookStep
void testBookStep(FeatureBitset features)
Definition: Flow_test.cpp:283
ripple::test::xrpMinusFee
jtx::PrettyAmount xrpMinusFee(jtx::Env const &env, std::int64_t xrpAmount)
Definition: Flow_test.cpp:53
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:216
ripple::OpenLedger::modify
bool modify(modify_type const &f)
Modify the open ledger.
Definition: OpenLedger.cpp:57
ripple::test::jtx::Env::current
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:299
ripple::test::Flow_test::testSelfPayLowQualityOffer
void testSelfPayLowQualityOffer(FeatureBitset features)
Definition: Flow_test.cpp:1206
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:115
ripple::test::Flow_test::testSelfPayment2
void testSelfPayment2(FeatureBitset features)
Definition: Flow_test.cpp:872
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::test::jtx::rate
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition: rate.cpp:30
ripple::test::Flow_test
Definition: Flow_test.cpp:60