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 flowJournal);
498 }();
499
500 BEAST_EXPECT(flowResult.removableOffers.size() == 1);
501 env.app().openLedger().modify(
502 [&](OpenView& view, beast::Journal j) {
503 if (flowResult.removableOffers.empty())
504 return false;
505 Sandbox sb(&view, tapNONE);
506 for (auto const& o : flowResult.removableOffers)
507 if (auto ok = sb.peek(keylet::offer(o)))
508 offerDelete(sb, ok, flowJournal);
509 sb.apply(view);
510 return true;
511 });
512
513 // used in payment, but since payment failed should be untouched
514 BEAST_EXPECT(isOffer(env, bob, BTC(50), USD(50)));
515 BEAST_EXPECT(isOffer(env, carol, BTC(1000), EUR(1)));
516 // found unfunded
517 BEAST_EXPECT(!isOffer(env, bob, BTC(60), EUR(50)));
518 }
519 {
520 // Do not produce more in the forward pass than the reverse pass
521 // This test uses a path that whose reverse pass will compute a
522 // 0.5 USD input required for a 1 EUR output. It sets a sendmax of
523 // 0.4 USD, so the payment engine will need to do a forward pass.
524 // Without limits, the 0.4 USD would produce 1000 EUR in the forward
525 // pass. This test checks that the payment produces 1 EUR, as
526 // expected.
527 Env env(*this, features);
528 env.fund(XRP(10000), alice, bob, carol, gw);
529 env.close();
530
531 env.trust(USD(1000), alice, bob, carol);
532 env.trust(EUR(1000), alice, bob, carol);
533
534 env(pay(gw, alice, USD(1000)));
535 env(pay(gw, bob, EUR(1000)));
536
537 Keylet const bobUsdOffer = keylet::offer(bob, env.seq(bob));
538 env(offer(bob, USD(1), drops(2)), txflags(tfPassive));
539 env(offer(bob, drops(1), EUR(1000)), txflags(tfPassive));
540
541 bool const reducedOffersV2 = features[fixReducedOffersV2];
542
543 // With reducedOffersV2, it is not allowed to accept less than
544 // USD(0.5) of bob's USD offer. If we provide 1 drop for less
545 // than USD(0.5), then the remaining fractional offer would
546 // block the order book.
547 TER const expectedTER =
548 reducedOffersV2 ? TER(tecPATH_DRY) : TER(tesSUCCESS);
549 env(pay(alice, carol, EUR(1)),
550 path(~XRP, ~EUR),
551 sendmax(USD(0.4)),
553 ter(expectedTER));
554
555 if (!reducedOffersV2)
556 {
557 env.require(balance(carol, EUR(1)));
558 env.require(balance(bob, USD(0.4)));
559 env.require(balance(bob, EUR(999)));
560
561 // Show that bob's USD offer is now a blocker.
562 std::shared_ptr<SLE const> const usdOffer = env.le(bobUsdOffer);
563 if (BEAST_EXPECT(usdOffer))
564 {
565 std::uint64_t const bookRate = [&usdOffer]() {
566 // Extract the least significant 64 bits from the
567 // book page. That's where the quality is stored.
568 std::string bookDirStr =
569 to_string(usdOffer->at(sfBookDirectory));
570 bookDirStr.erase(0, 48);
571 return std::stoull(bookDirStr, nullptr, 16);
572 }();
573 std::uint64_t const actualRate = getRate(
574 usdOffer->at(sfTakerGets), usdOffer->at(sfTakerPays));
575
576 // We expect the actual rate of the offer to be worse
577 // (larger) than the rate of the book page holding the
578 // offer. This is a defect which is corrected by
579 // fixReducedOffersV2.
580 BEAST_EXPECT(actualRate > bookRate);
581 }
582 }
583 }
584 }
585
586 void
588 {
589 testcase("Transfer Rate");
590
591 using namespace jtx;
592
593 auto const gw = Account("gateway");
594 auto const USD = gw["USD"];
595 auto const BTC = gw["BTC"];
596 auto const EUR = gw["EUR"];
597 Account const alice("alice");
598 Account const bob("bob");
599 Account const carol("carol");
600
601 {
602 // Simple payment through a gateway with a
603 // transfer rate
604 Env env(*this, features);
605
606 env.fund(XRP(10000), alice, bob, carol, gw);
607 env.close();
608 env(rate(gw, 1.25));
609 env.trust(USD(1000), alice, bob, carol);
610 env(pay(gw, alice, USD(50)));
611 env.require(balance(alice, USD(50)));
612 env(pay(alice, bob, USD(40)), sendmax(USD(50)));
613 env.require(balance(bob, USD(40)), balance(alice, USD(0)));
614 }
615 {
616 // transfer rate is not charged when issuer is src or dst
617 Env env(*this, features);
618
619 env.fund(XRP(10000), alice, bob, carol, gw);
620 env.close();
621 env(rate(gw, 1.25));
622 env.trust(USD(1000), alice, bob, carol);
623 env(pay(gw, alice, USD(50)));
624 env.require(balance(alice, USD(50)));
625 env(pay(alice, gw, USD(40)), sendmax(USD(40)));
626 env.require(balance(alice, USD(10)));
627 }
628 {
629 // transfer fee on an offer
630 Env env(*this, features);
631
632 env.fund(XRP(10000), alice, bob, carol, gw);
633 env.close();
634 env(rate(gw, 1.25));
635 env.trust(USD(1000), alice, bob, carol);
636 env(pay(gw, bob, USD(65)));
637
638 env(offer(bob, XRP(50), USD(50)));
639
640 env(pay(alice, carol, USD(50)), path(~USD), sendmax(XRP(50)));
641 env.require(
642 balance(alice, xrpMinusFee(env, 10000 - 50)),
643 balance(bob, USD(2.5)), // owner pays transfer fee
644 balance(carol, USD(50)));
645 }
646
647 {
648 // Transfer fee two consecutive offers
649 Env env(*this, features);
650
651 env.fund(XRP(10000), alice, bob, carol, gw);
652 env.close();
653 env(rate(gw, 1.25));
654 env.trust(USD(1000), alice, bob, carol);
655 env.trust(EUR(1000), alice, bob, carol);
656 env(pay(gw, bob, USD(50)));
657 env(pay(gw, bob, EUR(50)));
658
659 env(offer(bob, XRP(50), USD(50)));
660 env(offer(bob, USD(50), EUR(50)));
661
662 env(pay(alice, carol, EUR(40)), path(~USD, ~EUR), sendmax(XRP(40)));
663 env.require(
664 balance(alice, xrpMinusFee(env, 10000 - 40)),
665 balance(bob, USD(40)),
666 balance(bob, EUR(0)),
667 balance(carol, EUR(40)));
668 }
669
670 {
671 // First pass through a strand redeems, second pass issues, no
672 // offers limiting step is not an endpoint
673 Env env(*this, features);
674 auto const USDA = alice["USD"];
675 auto const USDB = bob["USD"];
676
677 env.fund(XRP(10000), alice, bob, carol, gw);
678 env.close();
679 env(rate(gw, 1.25));
680 env.trust(USD(1000), alice, bob, carol);
681 env.trust(USDA(1000), bob);
682 env.trust(USDB(1000), gw);
683 env(pay(gw, bob, USD(50)));
684 // alice -> bob -> gw -> carol. $50 should have transfer fee; $10,
685 // no fee
686 env(pay(alice, carol, USD(50)), path(bob), sendmax(USDA(60)));
687 env.require(
688 balance(bob, USD(-10)),
689 balance(bob, USDA(60)),
690 balance(carol, USD(50)));
691 }
692 {
693 // First pass through a strand redeems, second pass issues, through
694 // an offer limiting step is not an endpoint
695 Env env(*this, features);
696 auto const USDA = alice["USD"];
697 auto const USDB = bob["USD"];
698 Account const dan("dan");
699
700 env.fund(XRP(10000), alice, bob, carol, dan, gw);
701 env.close();
702 env(rate(gw, 1.25));
703 env.trust(USD(1000), alice, bob, carol, dan);
704 env.trust(EUR(1000), carol, dan);
705 env.trust(USDA(1000), bob);
706 env.trust(USDB(1000), gw);
707 env(pay(gw, bob, USD(50)));
708 env(pay(gw, dan, EUR(100)));
709 env(offer(dan, USD(100), EUR(100)));
710 // alice -> bob -> gw -> carol. $50 should have transfer fee; $10,
711 // no fee
712 env(pay(alice, carol, EUR(50)),
713 path(bob, gw, ~EUR),
714 sendmax(USDA(60)),
716 env.require(
717 balance(bob, USD(-10)),
718 balance(bob, USDA(60)),
719 balance(dan, USD(50)),
720 balance(dan, EUR(37.5)),
721 balance(carol, EUR(50)));
722 }
723
724 {
725 // Offer where the owner is also the issuer, owner pays fee
726 Env env(*this, features);
727
728 env.fund(XRP(10000), alice, bob, gw);
729 env.close();
730 env(rate(gw, 1.25));
731 env.trust(USD(1000), alice, bob);
732 env(offer(gw, XRP(100), USD(100)));
733 env(pay(alice, bob, USD(100)), sendmax(XRP(100)));
734 env.require(
735 balance(alice, xrpMinusFee(env, 10000 - 100)),
736 balance(bob, USD(100)));
737 }
738 if (!features[featureOwnerPaysFee])
739 {
740 // Offer where the owner is also the issuer, sender pays fee
741 Env env(*this, features);
742
743 env.fund(XRP(10000), alice, bob, gw);
744 env.close();
745 env(rate(gw, 1.25));
746 env.trust(USD(1000), alice, bob);
747 env(offer(gw, XRP(125), USD(125)));
748 env(pay(alice, bob, USD(100)), sendmax(XRP(200)));
749 env.require(
750 balance(alice, xrpMinusFee(env, 10000 - 125)),
751 balance(bob, USD(100)));
752 }
753 }
754
755 void
757 {
758 testcase("falseDryChanges");
759
760 using namespace jtx;
761
762 auto const gw = Account("gateway");
763 auto const USD = gw["USD"];
764 auto const EUR = gw["EUR"];
765 Account const alice("alice");
766 Account const bob("bob");
767 Account const carol("carol");
768
769 Env env(*this, features);
770
771 env.fund(XRP(10000), alice, carol, gw);
772 env.fund(reserve(env, 5), bob);
773 env.close();
774 env.trust(USD(1000), alice, bob, carol);
775 env.trust(EUR(1000), alice, bob, carol);
776
777 env(pay(gw, alice, EUR(50)));
778 env(pay(gw, bob, USD(50)));
779
780 // Bob has _just_ slightly less than 50 xrp available
781 // If his owner count changes, he will have more liquidity.
782 // This is one error case to test (when Flow is used).
783 // Computing the incoming xrp to the XRP/USD offer will require two
784 // recursive calls to the EUR/XRP offer. The second call will return
785 // tecPATH_DRY, but the entire path should not be marked as dry. This
786 // is the second error case to test (when flowV1 is used).
787 env(offer(bob, EUR(50), XRP(50)));
788 env(offer(bob, XRP(50), USD(50)));
789
790 env(pay(alice, carol, USD(1000000)),
791 path(~XRP, ~USD),
792 sendmax(EUR(500)),
794
795 auto const carolUSD = env.balance(carol, USD).value();
796 BEAST_EXPECT(carolUSD > USD(0) && carolUSD < USD(50));
797 }
798
799 void
801 {
802 // Single path with two offers and limit quality. The quality limit is
803 // such that the first offer should be taken but the second should not.
804 // The total amount delivered should be the sum of the two offers and
805 // sendMax should be more than the first offer.
806 testcase("limitQuality");
807 using namespace jtx;
808
809 auto const gw = Account("gateway");
810 auto const USD = gw["USD"];
811 Account const alice("alice");
812 Account const bob("bob");
813 Account const carol("carol");
814
815 {
816 Env env(*this);
817
818 env.fund(XRP(10000), alice, bob, carol, gw);
819 env.close();
820
821 env.trust(USD(100), alice, bob, carol);
822 env(pay(gw, bob, USD(100)));
823 env(offer(bob, XRP(50), USD(50)));
824 env(offer(bob, XRP(100), USD(50)));
825
826 env(pay(alice, carol, USD(100)),
827 path(~USD),
828 sendmax(XRP(100)),
830
831 env.require(balance(carol, USD(50)));
832 }
833 }
834
835 // Helper function that returns the reserve on an account based on
836 // the passed in number of owners.
837 static XRPAmount
839 {
840 return env.current()->fees().accountReserve(count);
841 }
842
843 // Helper function that returns the Offers on an account.
846 {
849 *env.current(),
850 account,
851 [&result](std::shared_ptr<SLE const> const& sle) {
852 if (sle->getType() == ltOFFER)
853 result.push_back(sle);
854 });
855 return result;
856 }
857
858 void
860 {
861 testcase("Self-payment 1");
862
863 // In this test case the new flow code mis-computes the amount
864 // of money to move. Fortunately the new code's re-execute
865 // check catches the problem and throws out the transaction.
866 //
867 // The old payment code handles the payment correctly.
868 using namespace jtx;
869
870 auto const gw1 = Account("gw1");
871 auto const gw2 = Account("gw2");
872 auto const alice = Account("alice");
873 auto const USD = gw1["USD"];
874 auto const EUR = gw2["EUR"];
875
876 Env env(*this, features);
877
878 env.fund(XRP(1000000), gw1, gw2);
879 env.close();
880
881 // The fee that's charged for transactions.
882 auto const f = env.current()->fees().base;
883
884 env.fund(reserve(env, 3) + f * 4, alice);
885 env.close();
886
887 env(trust(alice, USD(2000)));
888 env(trust(alice, EUR(2000)));
889 env.close();
890
891 env(pay(gw1, alice, USD(1)));
892 env(pay(gw2, alice, EUR(1000)));
893 env.close();
894
895 env(offer(alice, USD(500), EUR(600)));
896 env.close();
897
898 env.require(owners(alice, 3));
899 env.require(balance(alice, USD(1)));
900 env.require(balance(alice, EUR(1000)));
901
902 auto aliceOffers = offersOnAccount(env, alice);
903 BEAST_EXPECT(aliceOffers.size() == 1);
904 for (auto const& offerPtr : aliceOffers)
905 {
906 auto const offer = *offerPtr;
907 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
908 BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
909 BEAST_EXPECT(offer[sfTakerPays] == USD(500));
910 }
911
912 env(pay(alice, alice, EUR(600)),
913 sendmax(USD(500)),
915 env.close();
916
917 env.require(owners(alice, 3));
918 env.require(balance(alice, USD(1)));
919 env.require(balance(alice, EUR(1000)));
920 aliceOffers = offersOnAccount(env, alice);
921 BEAST_EXPECT(aliceOffers.size() == 1);
922 for (auto const& offerPtr : aliceOffers)
923 {
924 auto const offer = *offerPtr;
925 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
926 BEAST_EXPECT(offer[sfTakerGets] == EUR(598.8));
927 BEAST_EXPECT(offer[sfTakerPays] == USD(499));
928 }
929 }
930
931 void
933 {
934 testcase("Self-payment 2");
935
936 // In this case the difference between the old payment code and
937 // the new is the values left behind in the offer. Not saying either
938 // ios ring, they are just different.
939 using namespace jtx;
940
941 auto const gw1 = Account("gw1");
942 auto const gw2 = Account("gw2");
943 auto const alice = Account("alice");
944 auto const USD = gw1["USD"];
945 auto const EUR = gw2["EUR"];
946
947 Env env(*this, features);
948
949 env.fund(XRP(1000000), gw1, gw2);
950 env.close();
951
952 // The fee that's charged for transactions.
953 auto const f = env.current()->fees().base;
954
955 env.fund(reserve(env, 3) + f * 4, alice);
956 env.close();
957
958 env(trust(alice, USD(506)));
959 env(trust(alice, EUR(606)));
960 env.close();
961
962 env(pay(gw1, alice, USD(500)));
963 env(pay(gw2, alice, EUR(600)));
964 env.close();
965
966 env(offer(alice, USD(500), EUR(600)));
967 env.close();
968
969 env.require(owners(alice, 3));
970 env.require(balance(alice, USD(500)));
971 env.require(balance(alice, EUR(600)));
972
973 auto aliceOffers = offersOnAccount(env, alice);
974 BEAST_EXPECT(aliceOffers.size() == 1);
975 for (auto const& offerPtr : aliceOffers)
976 {
977 auto const offer = *offerPtr;
978 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
979 BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
980 BEAST_EXPECT(offer[sfTakerPays] == USD(500));
981 }
982
983 env(pay(alice, alice, EUR(60)),
984 sendmax(USD(50)),
986 env.close();
987
988 env.require(owners(alice, 3));
989 env.require(balance(alice, USD(500)));
990 env.require(balance(alice, EUR(600)));
991 aliceOffers = offersOnAccount(env, alice);
992 BEAST_EXPECT(aliceOffers.size() == 1);
993 for (auto const& offerPtr : aliceOffers)
994 {
995 auto const offer = *offerPtr;
996 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
997 BEAST_EXPECT(offer[sfTakerGets] == EUR(594));
998 BEAST_EXPECT(offer[sfTakerPays] == USD(495));
999 }
1000 }
1001 void
1002 testSelfFundedXRPEndpoint(bool consumeOffer, FeatureBitset features)
1003 {
1004 // Test that the deferred credit table is not bypassed for
1005 // XRPEndpointSteps. If the account in the first step is sending XRP and
1006 // that account also owns an offer that receives XRP, it should not be
1007 // possible for that step to use the XRP received in the offer as part
1008 // of the payment.
1009 testcase("Self funded XRPEndpoint");
1010
1011 using namespace jtx;
1012
1013 Env env(*this, features);
1014
1015 auto const alice = Account("alice");
1016 auto const gw = Account("gw");
1017 auto const USD = gw["USD"];
1018
1019 env.fund(XRP(10000), alice, gw);
1020 env.close();
1021 env(trust(alice, USD(20)));
1022 env(pay(gw, alice, USD(10)));
1023 env(offer(alice, XRP(50000), USD(10)));
1024
1025 // Consuming the offer changes the owner count, which could also cause
1026 // liquidity to decrease in the forward pass
1027 auto const toSend = consumeOffer ? USD(10) : USD(9);
1028 env(pay(alice, alice, toSend),
1029 path(~USD),
1030 sendmax(XRP(20000)),
1032 }
1033
1034 void
1036 {
1037 testcase("Unfunded Offer");
1038
1039 using namespace jtx;
1040 {
1041 // Test reverse
1042 Env env(*this, features);
1043
1044 auto const alice = Account("alice");
1045 auto const bob = Account("bob");
1046 auto const gw = Account("gw");
1047 auto const USD = gw["USD"];
1048
1049 env.fund(XRP(100000), alice, bob, gw);
1050 env.close();
1051 env(trust(bob, USD(20)));
1052
1053 STAmount tinyAmt1{
1054 USD.issue(),
1055 9000000000000000ll,
1056 -17,
1057 false,
1059 STAmount tinyAmt3{
1060 USD.issue(),
1061 9000000000000003ll,
1062 -17,
1063 false,
1065
1066 env(offer(gw, drops(9000000000), tinyAmt3));
1067 env(pay(alice, bob, tinyAmt1),
1068 path(~USD),
1069 sendmax(drops(9000000000)),
1071
1072 BEAST_EXPECT(!isOffer(env, gw, XRP(0), USD(0)));
1073 }
1074 {
1075 // Test forward
1076 Env env(*this, features);
1077
1078 auto const alice = Account("alice");
1079 auto const bob = Account("bob");
1080 auto const gw = Account("gw");
1081 auto const USD = gw["USD"];
1082
1083 env.fund(XRP(100000), alice, bob, gw);
1084 env.close();
1085 env(trust(alice, USD(20)));
1086
1087 STAmount tinyAmt1{
1088 USD.issue(),
1089 9000000000000000ll,
1090 -17,
1091 false,
1093 STAmount tinyAmt3{
1094 USD.issue(),
1095 9000000000000003ll,
1096 -17,
1097 false,
1099
1100 env(pay(gw, alice, tinyAmt1));
1101
1102 env(offer(gw, tinyAmt3, drops(9000000000)));
1103 env(pay(alice, bob, drops(9000000000)),
1104 path(~XRP),
1105 sendmax(USD(1)),
1107
1108 BEAST_EXPECT(!isOffer(env, gw, USD(0), XRP(0)));
1109 }
1110 }
1111
1112 void
1114 {
1115 testcase("ReexecuteDirectStep");
1116
1117 using namespace jtx;
1118 Env env(*this, features);
1119
1120 auto const alice = Account("alice");
1121 auto const bob = Account("bob");
1122 auto const gw = Account("gw");
1123 auto const USD = gw["USD"];
1124 auto const usdC = USD.currency;
1125
1126 env.fund(XRP(10000), alice, bob, gw);
1127 env.close();
1128 env(trust(alice, USD(100)));
1129 env.close();
1130
1131 BEAST_EXPECT(!getNoRippleFlag(env, gw, alice, usdC));
1132
1133 env(pay(
1134 gw,
1135 alice,
1136 // 12.55....
1137 STAmount{
1138 USD.issue(), std::uint64_t(1255555555555555ull), -14, false}));
1139
1140 env(offer(
1141 gw,
1142 // 5.0...
1143 STAmount{
1144 USD.issue(), std::uint64_t(5000000000000000ull), -15, false},
1145 XRP(1000)));
1146
1147 env(offer(
1148 gw,
1149 // .555...
1150 STAmount{
1151 USD.issue(), std::uint64_t(5555555555555555ull), -16, false},
1152 XRP(10)));
1153
1154 env(offer(
1155 gw,
1156 // 4.44....
1157 STAmount{
1158 USD.issue(), std::uint64_t(4444444444444444ull), -15, false},
1159 XRP(.1)));
1160
1161 env(offer(
1162 alice,
1163 // 17
1164 STAmount{
1165 USD.issue(), std::uint64_t(1700000000000000ull), -14, false},
1166 XRP(.001)));
1167
1168 env(pay(alice, bob, XRP(10000)),
1169 path(~XRP),
1170 sendmax(USD(100)),
1172 }
1173
1174 void
1176 {
1177 testcase("ripd1443");
1178
1179 using namespace jtx;
1180 Env env(*this);
1181 auto const alice = Account("alice");
1182 auto const bob = Account("bob");
1183 auto const carol = Account("carol");
1184 auto const gw = Account("gw");
1185
1186 env.fund(XRP(100000000), alice, noripple(bob), carol, gw);
1187 env.close();
1188 env.trust(gw["USD"](10000), alice, carol);
1189 env(trust(bob, gw["USD"](10000), tfSetNoRipple));
1190 env.trust(gw["USD"](10000), bob);
1191 env.close();
1192
1193 // set no ripple between bob and the gateway
1194
1195 env(pay(gw, alice, gw["USD"](1000)));
1196 env.close();
1197
1198 env(offer(alice, bob["USD"](1000), XRP(1)));
1199 env.close();
1200
1201 env(pay(alice, alice, XRP(1)),
1202 path(gw, bob, ~XRP),
1203 sendmax(gw["USD"](1000)),
1205 ter(tecPATH_DRY));
1206 env.close();
1207
1208 env.trust(bob["USD"](10000), alice);
1209 env(pay(bob, alice, bob["USD"](1000)));
1210
1211 env(offer(alice, XRP(1000), bob["USD"](1000)));
1212 env.close();
1213
1214 env(pay(carol, carol, gw["USD"](1000)),
1215 path(~bob["USD"], gw),
1216 sendmax(XRP(100000)),
1218 ter(tecPATH_DRY));
1219 env.close();
1220
1221 pass();
1222 }
1223
1224 void
1226 {
1227 testcase("ripd1449");
1228
1229 using namespace jtx;
1230 Env env(*this);
1231
1232 // pay alice -> xrp -> USD/bob -> bob -> gw -> alice
1233 // set no ripple on bob's side of the bob/gw trust line
1234 // carol has the bob/USD and makes an offer, bob has USD/gw
1235
1236 auto const alice = Account("alice");
1237 auto const bob = Account("bob");
1238 auto const carol = Account("carol");
1239 auto const gw = Account("gw");
1240 auto const USD = gw["USD"];
1241
1242 env.fund(XRP(100000000), alice, bob, carol, gw);
1243 env.close();
1244 env.trust(USD(10000), alice, carol);
1245 env(trust(bob, USD(10000), tfSetNoRipple));
1246 env.trust(USD(10000), bob);
1247 env.trust(bob["USD"](10000), carol);
1248 env.close();
1249
1250 env(pay(bob, carol, bob["USD"](1000)));
1251 env(pay(gw, bob, USD(1000)));
1252 env.close();
1253
1254 env(offer(carol, XRP(1), bob["USD"](1000)));
1255 env.close();
1256
1257 env(pay(alice, alice, USD(1000)),
1258 path(~bob["USD"], bob, gw),
1259 sendmax(XRP(1)),
1261 ter(tecPATH_DRY));
1262 env.close();
1263 }
1264
1265 void
1267 {
1268 // The new payment code used to assert if an offer was made for more
1269 // XRP than the offering account held. This unit test reproduces
1270 // that failing case.
1271 testcase("Self crossing low quality offer");
1272
1273 using namespace jtx;
1274
1275 Env env(*this, features);
1276
1277 auto const ann = Account("ann");
1278 auto const gw = Account("gateway");
1279 auto const CTB = gw["CTB"];
1280
1281 auto const fee = env.current()->fees().base;
1282 env.fund(reserve(env, 2) + drops(9999640) + fee, ann);
1283 env.fund(reserve(env, 2) + fee * 4, gw);
1284 env.close();
1285
1286 env(rate(gw, 1.002));
1287 env(trust(ann, CTB(10)));
1288 env.close();
1289
1290 env(pay(gw, ann, CTB(2.856)));
1291 env.close();
1292
1293 env(offer(ann, drops(365611702030), CTB(5.713)));
1294 env.close();
1295
1296 // This payment caused the assert.
1297 env(pay(ann, ann, CTB(0.687)),
1298 sendmax(drops(20000000000)),
1300 }
1301
1302 void
1304 {
1305 testcase("Empty Strand");
1306 using namespace jtx;
1307
1308 auto const alice = Account("alice");
1309
1310 Env env(*this, features);
1311
1312 env.fund(XRP(10000), alice);
1313 env.close();
1314
1315 env(pay(alice, alice, alice["USD"](100)),
1316 path(~alice["USD"]),
1317 ter(temBAD_PATH));
1318 }
1319
1320 void
1322 {
1323 testcase("Circular XRP");
1324
1325 using namespace jtx;
1326 auto const alice = Account("alice");
1327 auto const bob = Account("bob");
1328 auto const gw = Account("gw");
1329 auto const USD = gw["USD"];
1330 auto const EUR = gw["EUR"];
1331
1332 for (auto const withFix : {true, false})
1333 {
1334 auto const feats = [&withFix]() -> FeatureBitset {
1335 if (withFix)
1336 return supported_amendments();
1337 return supported_amendments() - FeatureBitset{fix1781};
1338 }();
1339 {
1340 // Payment path starting with XRP
1341 Env env(*this, feats);
1342 env.fund(XRP(10000), alice, bob, gw);
1343 env.close();
1344 env.trust(USD(1000), alice, bob);
1345 env.trust(EUR(1000), alice, bob);
1346 env.close();
1347 env(pay(gw, alice, USD(100)));
1348 env(pay(gw, alice, EUR(100)));
1349 env.close();
1350
1351 env(offer(alice, XRP(100), USD(100)), txflags(tfPassive));
1352 env(offer(alice, USD(100), XRP(100)), txflags(tfPassive));
1353 env(offer(alice, XRP(100), EUR(100)), txflags(tfPassive));
1354 env.close();
1355
1356 TER const expectedTer =
1357 withFix ? TER{temBAD_PATH_LOOP} : TER{tesSUCCESS};
1358 env(pay(alice, bob, EUR(1)),
1359 path(~USD, ~XRP, ~EUR),
1360 sendmax(XRP(1)),
1362 ter(expectedTer));
1363 }
1364 pass();
1365 }
1366 {
1367 // Payment path ending with XRP
1368 Env env(*this);
1369 env.fund(XRP(10000), alice, bob, gw);
1370 env.close();
1371 env.trust(USD(1000), alice, bob);
1372 env.trust(EUR(1000), alice, bob);
1373 env(pay(gw, alice, USD(100)));
1374 env(pay(gw, alice, EUR(100)));
1375 env.close();
1376
1377 env(offer(alice, XRP(100), USD(100)), txflags(tfPassive));
1378 env(offer(alice, EUR(100), XRP(100)), txflags(tfPassive));
1379 env.close();
1380 // EUR -> //XRP -> //USD ->XRP
1381 env(pay(alice, bob, XRP(1)),
1382 path(~XRP, ~USD, ~XRP),
1383 sendmax(EUR(1)),
1386 }
1387 {
1388 // Payment where loop is formed in the middle of the path, not on an
1389 // endpoint
1390 auto const JPY = gw["JPY"];
1391 Env env(*this);
1392 env.fund(XRP(10000), alice, bob, gw);
1393 env.close();
1394 env.trust(USD(1000), alice, bob);
1395 env.trust(EUR(1000), alice, bob);
1396 env.trust(JPY(1000), alice, bob);
1397 env.close();
1398 env(pay(gw, alice, USD(100)));
1399 env(pay(gw, alice, EUR(100)));
1400 env(pay(gw, alice, JPY(100)));
1401 env.close();
1402
1403 env(offer(alice, USD(100), XRP(100)), txflags(tfPassive));
1404 env(offer(alice, XRP(100), EUR(100)), txflags(tfPassive));
1405 env(offer(alice, EUR(100), XRP(100)), txflags(tfPassive));
1406 env(offer(alice, XRP(100), JPY(100)), txflags(tfPassive));
1407 env.close();
1408
1409 env(pay(alice, bob, JPY(1)),
1410 path(~XRP, ~EUR, ~XRP, ~JPY),
1411 sendmax(USD(1)),
1414 }
1415 }
1416
1417 void
1419 {
1420 testcase("Payment with ticket");
1421 using namespace jtx;
1422
1423 auto const alice = Account("alice");
1424 auto const bob = Account("bob");
1425
1426 Env env(*this, features);
1427
1428 env.fund(XRP(10000), alice);
1429 env.close();
1430
1431 // alice creates a ticket for the payment.
1432 std::uint32_t const ticketSeq{env.seq(alice) + 1};
1433 env(ticket::create(alice, 1));
1434
1435 // Make a payment using the ticket.
1436 env(pay(alice, bob, XRP(1000)), ticket::use(ticketSeq));
1437 env.close();
1438 env.require(balance(bob, XRP(1000)));
1439 env.require(
1440 balance(alice, XRP(9000) - (env.current()->fees().base * 2)));
1441 }
1442
1443 void
1445 {
1446 using namespace jtx;
1447 FeatureBitset const ownerPaysFee{featureOwnerPaysFee};
1448 FeatureBitset const reducedOffersV2(fixReducedOffersV2);
1449
1450 testLineQuality(features);
1451 testFalseDry(features);
1452 testBookStep(features - reducedOffersV2);
1453 testDirectStep(features);
1454 testBookStep(features);
1455 testDirectStep(features | ownerPaysFee);
1456 testBookStep(features | ownerPaysFee);
1457 testTransferRate(features | ownerPaysFee);
1458 testSelfPayment1(features);
1459 testSelfPayment2(features);
1460 testSelfFundedXRPEndpoint(false, features);
1461 testSelfFundedXRPEndpoint(true, features);
1462 testUnfundedOffer(features);
1463 testReexecuteDirectStep(features);
1465 testTicketPay(features);
1466 }
1467
1468 void
1469 run() override
1470 {
1473 testRIPD1443();
1474 testRIPD1449();
1475
1476 using namespace jtx;
1477 auto const sa = supported_amendments();
1478 testWithFeats(sa - featureFlowCross);
1479 testWithFeats(sa);
1480 testEmptyStrand(sa);
1481 }
1482};
1483
1485{
1486 void
1487 run() override
1488 {
1489 using namespace jtx;
1490 auto const all = supported_amendments();
1491 FeatureBitset const flowCross{featureFlowCross};
1492 FeatureBitset const f1513{fix1513};
1493
1494 testWithFeats(all - flowCross - f1513);
1495 testWithFeats(all - flowCross);
1496 testWithFeats(all - f1513);
1498
1499 testEmptyStrand(all - f1513);
1501 }
1502};
1503
1504BEAST_DEFINE_TESTSUITE_PRIO(Flow, app, ripple, 2);
1505BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Flow_manual, app, ripple, 4);
1506
1507} // namespace test
1508} // 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:56
Writable ledger view that accumulates state and tx changes.
Definition: OpenView.h:57
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:212
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:535
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:264
Application & app()
Definition: Env.h:261
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:233
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:221
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:237
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:267
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:96
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition: View.cpp:1459
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition: STAmount.cpp:486
@ no
Definition: Steps.h:44
constexpr std::uint32_t tfPartialPayment
Definition: TxFlags.h:105
@ tecPATH_PARTIAL
Definition: TER.h:282
@ tecPATH_DRY
Definition: TER.h:294
constexpr std::uint32_t tfNoRippleDirect
Definition: TxFlags.h:104
@ tesSUCCESS
Definition: TER.h:244
constexpr std::uint32_t tfLimitQuality
Definition: TxFlags.h:106
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:656
@ tapNONE
Definition: ApplyView.h:32
TERSubset< CanCvtToTER > TER
Definition: TER.h:643
constexpr std::uint32_t tfSetNoRipple
Definition: TxFlags.h:113
@ 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:1487
void run() override
Runs the suite.
Definition: Flow_test.cpp:1469
void testWithFeats(FeatureBitset features)
Definition: Flow_test.cpp:1444
void testSelfPayment1(FeatureBitset features)
Definition: Flow_test.cpp:859
void testTransferRate(FeatureBitset features)
Definition: Flow_test.cpp:587
static XRPAmount reserve(jtx::Env &env, std::uint32_t count)
Definition: Flow_test.cpp:838
void testBookStep(FeatureBitset features)
Definition: Flow_test.cpp:287
void testUnfundedOffer(FeatureBitset features)
Definition: Flow_test.cpp:1035
void testTicketPay(FeatureBitset features)
Definition: Flow_test.cpp:1418
void testSelfFundedXRPEndpoint(bool consumeOffer, FeatureBitset features)
Definition: Flow_test.cpp:1002
void testEmptyStrand(FeatureBitset features)
Definition: Flow_test.cpp:1303
void testSelfPayLowQualityOffer(FeatureBitset features)
Definition: Flow_test.cpp:1266
static std::vector< std::shared_ptr< SLE const > > offersOnAccount(jtx::Env &env, jtx::Account account)
Definition: Flow_test.cpp:845
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:756
void testSelfPayment2(FeatureBitset features)
Definition: Flow_test.cpp:932
void testReexecuteDirectStep(FeatureBitset features)
Definition: Flow_test.cpp:1113
STAmount const & value() const