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