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