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