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