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