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