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