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