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