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