xrpld
Loading...
Searching...
No Matches
ReducedOffer_test.cpp
1
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/balance.h> // IWYU pragma: keep
6#include <test/jtx/fee.h>
7#include <test/jtx/offer.h>
8#include <test/jtx/owners.h>
9#include <test/jtx/pay.h>
10#include <test/jtx/trust.h>
11#include <test/jtx/txflags.h>
12
13#include <xrpl/beast/unit_test/suite.h>
14#include <xrpl/json/json_value.h>
15#include <xrpl/json/to_string.h>
16#include <xrpl/protocol/Feature.h>
17#include <xrpl/protocol/Quality.h>
18#include <xrpl/protocol/SField.h>
19#include <xrpl/protocol/STAmount.h>
20#include <xrpl/protocol/TxFlags.h>
21#include <xrpl/protocol/jss.h>
22
23#include <cstdint>
24#include <initializer_list>
25#include <utility>
26
27namespace xrpl::test {
28
30{
31 static auto
33 {
34 json::Value jvParams;
35 jvParams[jss::offer][jss::account] = acct.human();
36 jvParams[jss::offer][jss::seq] = offerSeq;
37 return env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
38 }
39
40 static bool
41 offerInLedger(jtx::Env& env, jtx::Account const& acct, std::uint32_t offerSeq)
42 {
43 json::Value ledgerOffer = ledgerEntryOffer(env, acct, offerSeq);
44 return !(
45 ledgerOffer.isMember(jss::error) &&
46 ledgerOffer[jss::error].asString() == "entryNotFound");
47 }
48
49 // Common code to clean up unneeded offers.
50 static void
52 jtx::Env& env,
54 {
55 for (auto [acct, offerSeq] : list)
56 env(offerCancel(acct, offerSeq));
57 env.close();
58 }
59
60public:
61 void
63 {
64 testcase("exercise partial cross new XRP/IOU offer Q change");
65
66 using namespace jtx;
67
68 auto const gw = Account{"gateway"};
69 auto const alice = Account{"alice"};
70 auto const bob = Account{"bob"};
71 auto const usd = gw["USD"];
72
73 {
74 Env env{*this, testableAmendments()};
75
76 // Make sure none of the offers we generate are under funded.
77 env.fund(XRP(10'000'000), gw, alice, bob);
78 env.close();
79
80 env(trust(alice, usd(10'000'000)));
81 env(trust(bob, usd(10'000'000)));
82 env.close();
83
84 env(pay(gw, bob, usd(10'000'000)));
85 env.close();
86
87 // Lambda that:
88 // 1. Exercises one offer pair,
89 // 2. Collects the results, and
90 // 3. Cleans up for the next offer pair.
91 // Returns 1 if the crossed offer has a bad rate for the book.
92 auto exerciseOfferPair = [this, &env, &alice, &bob](
93 Amounts const& inLedger,
94 Amounts const& newOffer) -> unsigned int {
95 // Put inLedger offer in the ledger so newOffer can cross it.
96 std::uint32_t const aliceOfferSeq = env.seq(alice);
97 env(offer(alice, inLedger.in, inLedger.out));
98 env.close();
99
100 // Now alice's offer will partially cross bob's offer.
101 STAmount const initialRate = Quality(newOffer).rate();
102 std::uint32_t const bobOfferSeq = env.seq(bob);
103 STAmount const bobInitialBalance = env.balance(bob);
104 STAmount const bobFee = env.current()->fees().base;
105 env(offer(bob, newOffer.in, newOffer.out, tfSell), Fee(bobFee));
106 env.close();
107 STAmount const bobFinalBalance = env.balance(bob);
108
109 // alice's offer should be fully crossed and so gone from
110 // the ledger.
111 if (!BEAST_EXPECT(!offerInLedger(env, alice, aliceOfferSeq)))
112 {
113 // If the in-ledger offer was not consumed then further
114 // results are meaningless.
115 return 1;
116 }
117
118 // bob's offer should be in the ledger, but reduced in size.
119 unsigned int badRate = 1;
120 {
121 json::Value bobOffer = ledgerEntryOffer(env, bob, bobOfferSeq);
122
123 STAmount const reducedTakerGets =
124 amountFromJson(sfTakerGets, bobOffer[jss::node][sfTakerGets.jsonName]);
125 STAmount const reducedTakerPays =
126 amountFromJson(sfTakerPays, bobOffer[jss::node][sfTakerPays.jsonName]);
127 STAmount const bobGot = env.balance(bob) + bobFee - bobInitialBalance;
128 BEAST_EXPECT(reducedTakerPays < newOffer.in);
129 BEAST_EXPECT(reducedTakerGets < newOffer.out);
130 STAmount const inLedgerRate =
131 Quality(Amounts{reducedTakerPays, reducedTakerGets}).rate();
132
133 badRate = inLedgerRate > initialRate ? 1 : 0;
134
135 // If the inLedgerRate is less than initial rate, then
136 // incrementing the mantissa of the reduced taker pays
137 // should result in a rate higher than initial. Check
138 // this to verify that the largest allowable TakerPays
139 // was computed.
140 if (badRate == 0)
141 {
142 STAmount const tweakedTakerPays = reducedTakerPays + drops(1);
143 STAmount const tweakedRate =
144 Quality(Amounts{tweakedTakerPays, reducedTakerGets}).rate();
145 BEAST_EXPECT(tweakedRate > initialRate);
146 }
147#if 0
148 std::cout << "Placed rate: " << initialRate
149 << "; in-ledger rate: " << inLedgerRate
150 << "; TakerPays: " << reducedTakerPays
151 << "; TakerGets: " << reducedTakerGets
152 << "; bob already got: " << bobGot << std::endl;
153// #else
154 std::string_view filler =
155 inLedgerRate > initialRate ? "**" : " ";
156 std::cout << "| `" << reducedTakerGets << "` | `"
157 << reducedTakerPays << "` | `" << initialRate
158 << "` | " << filler << "`" << inLedgerRate << "`"
159 << filler << " |`" << std::endl;
160#endif
161 }
162
163 // In preparation for the next iteration make sure the two
164 // offers are gone from the ledger.
165 cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
166 return badRate;
167 };
168
169 // bob's offer (the new offer) is the same every time:
170 Amounts const bobOffer{STAmount(XRP(1)), STAmount(usd, 1, 0)};
171
172 // alice's offer has a slightly smaller TakerPays with each
173 // iteration. This should mean that the size of the offer bob
174 // places in the ledger should increase with each iteration.
175 unsigned int blockedCount = 0;
176 for (std::uint64_t mantissaReduce = 1'000'000'000ull;
177 mantissaReduce <= 5'000'000'000ull;
178 mantissaReduce += 20'000'000ull)
179 {
180 STAmount const aliceUSD{
181 bobOffer.out.asset(),
182 bobOffer.out.mantissa() - mantissaReduce,
183 bobOffer.out.exponent()};
184 STAmount const aliceXRP{bobOffer.in.asset(), bobOffer.in.mantissa() - 1};
185 Amounts const aliceOffer{aliceUSD, aliceXRP};
186 blockedCount += exerciseOfferPair(aliceOffer, bobOffer);
187 }
188
189 // None of the test cases should produce a potentially blocking
190 // rate.
191 BEAST_EXPECT(blockedCount == 0);
192 }
193 }
194
195 void
197 {
198 testcase("exercise partial cross old XRP/IOU offer Q change");
199
200 using namespace jtx;
201
202 auto const gw = Account{"gateway"};
203 auto const alice = Account{"alice"};
204 auto const bob = Account{"bob"};
205 auto const usd = gw["USD"];
206
207 {
208 // Make sure none of the offers we generate are under funded.
209 Env env{*this, testableAmendments()};
210 env.fund(XRP(10'000'000), gw, alice, bob);
211 env.close();
212
213 env(trust(alice, usd(10'000'000)));
214 env(trust(bob, usd(10'000'000)));
215 env.close();
216
217 env(pay(gw, alice, usd(10'000'000)));
218 env.close();
219
220 // Lambda that:
221 // 1. Exercises one offer pair,
222 // 2. Collects the results, and
223 // 3. Cleans up for the next offer pair.
224 auto exerciseOfferPair = [this, &env, &alice, &bob](
225 Amounts const& inLedger,
226 Amounts const& newOffer) -> unsigned int {
227 // Get the inLedger offer into the ledger so newOffer can cross
228 // it.
229 STAmount const initialRate = Quality(inLedger).rate();
230 std::uint32_t const aliceOfferSeq = env.seq(alice);
231 env(offer(alice, inLedger.in, inLedger.out));
232 env.close();
233
234 // Now bob's offer will partially cross alice's offer.
235 std::uint32_t const bobOfferSeq = env.seq(bob);
236 STAmount const aliceInitialBalance = env.balance(alice);
237 env(offer(bob, newOffer.in, newOffer.out));
238 env.close();
239 STAmount const aliceFinalBalance = env.balance(alice);
240
241 // bob's offer should not have made it into the ledger.
242 if (!BEAST_EXPECT(!offerInLedger(env, bob, bobOfferSeq)))
243 {
244 // If the in-ledger offer was not consumed then further
245 // results are meaningless.
246 cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
247 return 1;
248 }
249 // alice's offer should still be in the ledger, but reduced in
250 // size.
251 unsigned int badRate = 1;
252 {
253 json::Value aliceOffer = ledgerEntryOffer(env, alice, aliceOfferSeq);
254
255 STAmount const reducedTakerGets =
256 amountFromJson(sfTakerGets, aliceOffer[jss::node][sfTakerGets.jsonName]);
257 STAmount const reducedTakerPays =
258 amountFromJson(sfTakerPays, aliceOffer[jss::node][sfTakerPays.jsonName]);
259 STAmount const aliceGot = env.balance(alice) - aliceInitialBalance;
260 BEAST_EXPECT(reducedTakerPays < inLedger.in);
261 BEAST_EXPECT(reducedTakerGets < inLedger.out);
262 STAmount const inLedgerRate =
263 Quality(Amounts{reducedTakerPays, reducedTakerGets}).rate();
264 badRate = inLedgerRate > initialRate ? 1 : 0;
265
266 // If the inLedgerRate is less than initial rate, then
267 // incrementing the mantissa of the reduced taker pays
268 // should result in a rate higher than initial. Check
269 // this to verify that the largest allowable TakerPays
270 // was computed.
271 if (badRate == 0)
272 {
273 STAmount const tweakedTakerPays = reducedTakerPays + drops(1);
274 STAmount const tweakedRate =
275 Quality(Amounts{tweakedTakerPays, reducedTakerGets}).rate();
276 BEAST_EXPECT(tweakedRate > initialRate);
277 }
278#if 0
279 std::cout << "Placed rate: " << initialRate
280 << "; in-ledger rate: " << inLedgerRate
281 << "; TakerPays: " << reducedTakerPays
282 << "; TakerGets: " << reducedTakerGets
283 << "; alice already got: " << aliceGot
284 << std::endl;
285// #else
286 std::string_view filler = badRate ? "**" : " ";
287 std::cout << "| `" << reducedTakerGets << "` | `"
288 << reducedTakerPays << "` | `" << initialRate
289 << "` | " << filler << "`" << inLedgerRate << "`"
290 << filler << " | `" << aliceGot << "` |"
291 << std::endl;
292#endif
293 }
294
295 // In preparation for the next iteration make sure the two
296 // offers are gone from the ledger.
297 cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
298 return badRate;
299 };
300
301 // alice's offer (the old offer) is the same every time:
302 Amounts const aliceOffer{STAmount(XRP(1)), STAmount(usd, 1, 0)};
303
304 // bob's offer has a slightly smaller TakerPays with each iteration.
305 // This should mean that the size of the offer alice leaves in the
306 // ledger should increase with each iteration.
307 unsigned int blockedCount = 0;
308 for (std::uint64_t mantissaReduce = 1'000'000'000ull;
309 mantissaReduce <= 4'000'000'000ull;
310 mantissaReduce += 20'000'000ull)
311 {
312 STAmount const bobUSD{
313 aliceOffer.out.asset(),
314 aliceOffer.out.mantissa() - mantissaReduce,
315 aliceOffer.out.exponent()};
316 STAmount const bobXRP{aliceOffer.in.asset(), aliceOffer.in.mantissa() - 1};
317 Amounts const bobOffer{bobUSD, bobXRP};
318
319 blockedCount += exerciseOfferPair(aliceOffer, bobOffer);
320 }
321
322 // None of the test cases should produce a potentially blocking
323 // rate.
324 BEAST_EXPECT(blockedCount == 0);
325 }
326 }
327
328 void
330 {
331 testcase("exercise underfunded XRP/IOU offer Q change");
332
333 // Bob places an offer that is not fully funded.
334
335 using namespace jtx;
336 auto const alice = Account{"alice"};
337 auto const bob = Account{"bob"};
338 auto const gw = Account{"gw"};
339 auto const usd = gw["USD"];
340
341 {
342 Env env{*this, testableAmendments()};
343
344 env.fund(XRP(10000), alice, bob, gw);
345 env.close();
346 env.trust(usd(1000), alice, bob);
347
348 int blockedOrderBookCount = 0;
349 for (STAmount initialBobUSD = usd(0.45); initialBobUSD <= usd(1);
350 initialBobUSD += usd(0.025))
351 {
352 // underfund bob's offer
353 env(pay(gw, bob, initialBobUSD));
354 env.close();
355
356 std::uint32_t const bobOfferSeq = env.seq(bob);
357 env(offer(bob, drops(2), usd(1)));
358 env.close();
359
360 // alice places an offer that would cross bob's if bob's were
361 // well funded.
362 std::uint32_t const aliceOfferSeq = env.seq(alice);
363 env(offer(alice, usd(1), drops(2)));
364 env.close();
365
366 // We want to detect order book blocking. If:
367 // 1. bob's offer is still in the ledger and
368 // 2. alice received no USD
369 // then we use that as evidence that bob's offer blocked the
370 // order book.
371 {
372 bool const bobOfferGone = !offerInLedger(env, bob, bobOfferSeq);
373 STAmount const aliceBalanceUSD = env.balance(alice, usd);
374
375 // Sanity check the ledger if alice got USD.
376 if (aliceBalanceUSD.signum() > 0)
377 {
378 BEAST_EXPECT(aliceBalanceUSD == initialBobUSD);
379 BEAST_EXPECT(env.balance(bob, usd) == usd(0));
380 BEAST_EXPECT(bobOfferGone);
381 }
382
383 // Track occurrences of order book blocking.
384 if (!bobOfferGone && aliceBalanceUSD.signum() == 0)
385 {
386 ++blockedOrderBookCount;
387 }
388
389 // In preparation for the next iteration clean up any
390 // leftover offers.
391 cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
392
393 // Zero out alice's and bob's USD balances.
394 if (STAmount const aliceBalance = env.balance(alice, usd);
395 aliceBalance.signum() > 0)
396 env(pay(alice, gw, aliceBalance));
397
398 if (STAmount const bobBalance = env.balance(bob, usd); bobBalance.signum() > 0)
399 env(pay(bob, gw, bobBalance));
400
401 env.close();
402 }
403 }
404
405 // None of the test cases should produce a potentially blocking
406 // rate.
407 BEAST_EXPECT(blockedOrderBookCount == 0);
408 }
409 }
410
411 void
413 {
414 testcase("exercise underfunded IOU/IOU offer Q change");
415
416 // Bob places an IOU/IOU offer that is not fully funded.
417
418 using namespace jtx;
419 using namespace std::chrono_literals;
420 auto const alice = Account{"alice"};
421 auto const bob = Account{"bob"};
422 auto const gw = Account{"gw"};
423
424 auto const usd = gw["USD"];
425 auto const eur = gw["EUR"];
426
427 STAmount const tinyUSD(usd, /*mantissa*/ 1, /*exponent*/ -81);
428
429 {
430 Env env{*this, testableAmendments()};
431
432 env.fund(XRP(10000), alice, bob, gw);
433 env.close();
434 env.trust(usd(1000), alice, bob);
435 env.trust(eur(1000), alice, bob);
436
437 STAmount const eurOffer(eur, /*mantissa*/ 2957, /*exponent*/ -76);
438 STAmount const usdOffer(usd, /*mantissa*/ 7109, /*exponent*/ -76);
439
440 STAmount const endLoop(usd, /*mantissa*/ 50, /*exponent*/ -81);
441
442 int blockedOrderBookCount = 0;
443 for (STAmount initialBobUSD = tinyUSD; initialBobUSD <= endLoop;
444 initialBobUSD += tinyUSD)
445 {
446 // underfund bob's offer
447 env(pay(gw, bob, initialBobUSD));
448 env(pay(gw, alice, eur(100)));
449 env.close();
450
451 // This offer is underfunded
452 std::uint32_t bobOfferSeq = env.seq(bob);
453 env(offer(bob, eurOffer, usdOffer));
454 env.close();
455 env.require(offers(bob, 1));
456
457 // alice places an offer that crosses bob's.
458 std::uint32_t aliceOfferSeq = env.seq(alice);
459 env(offer(alice, usdOffer, eurOffer));
460 env.close();
461
462 // Examine the aftermath of alice's offer.
463 {
464 bool const bobOfferGone = !offerInLedger(env, bob, bobOfferSeq);
465 STAmount const aliceBalanceUSD = env.balance(alice, usd);
466#if 0
468 << "bob initial: " << initialBobUSD
469 << "; alice final: " << aliceBalanceUSD
470 << "; bob offer: " << bobOfferJson.toStyledString()
471 << std::endl;
472#endif
473 // Sanity check the ledger if alice got USD.
474 if (aliceBalanceUSD.signum() > 0)
475 {
476 BEAST_EXPECT(aliceBalanceUSD == initialBobUSD);
477 BEAST_EXPECT(env.balance(bob, usd) == usd(0));
478 BEAST_EXPECT(bobOfferGone);
479 }
480
481 // Track occurrences of order book blocking.
482 if (!bobOfferGone && aliceBalanceUSD.signum() == 0)
483 {
484 ++blockedOrderBookCount;
485 }
486 }
487
488 // In preparation for the next iteration clean up any
489 // leftover offers.
490 cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
491
492 // Zero out alice's and bob's IOU balances.
493 auto zeroBalance = [&env, &gw](Account const& acct, IOU const& iou) {
494 if (STAmount const balance = env.balance(acct, iou); balance.signum() > 0)
495 env(pay(acct, gw, balance));
496 };
497
498 zeroBalance(alice, eur);
499 zeroBalance(alice, usd);
500 zeroBalance(bob, eur);
501 zeroBalance(bob, usd);
502 env.close();
503 }
504
505 // None of the test cases should produce a potentially blocking
506 // rate.
507 BEAST_EXPECT(blockedOrderBookCount == 0);
508 }
509 }
510
511 static Amounts
513 {
514 STAmount const in = amountFromJson(sfTakerPays, json[sfTakerPays.jsonName]);
515 STAmount const out = amountFromJson(sfTakerGets, json[sfTakerGets.jsonName]);
516 return {in, out};
517 }
518
519 void
521 {
522 // This test case was motivated by Issue #4937. It recreates
523 // the specific failure identified in that issue and samples some other
524 // cases in the same vicinity to make sure that the new behavior makes
525 // sense.
526 testcase("exercise tfSell partial cross old XRP/IOU offer Q change");
527
528 using namespace jtx;
529
530 Account const gw("gateway");
531 Account const alice("alice");
532 Account const bob("bob");
533 Account const carol("carol");
534 auto const usd = gw["USD"];
535
536 // Make one test run without fixReducedOffersV2 and one with.
537 for (FeatureBitset features :
538 {testableAmendments() - fixReducedOffersV2, testableAmendments() | fixReducedOffersV2})
539 {
540 // Make sure none of the offers we generate are under funded.
541 Env env{*this, features};
542 env.fund(XRP(10'000'000), gw, alice, bob, carol);
543 env.close();
544
545 env(trust(alice, usd(10'000'000)));
546 env(trust(bob, usd(10'000'000)));
547 env(trust(carol, usd(10'000'000)));
548 env.close();
549
550 env(pay(gw, alice, usd(10'000'000)));
551 env(pay(gw, bob, usd(10'000'000)));
552 env(pay(gw, carol, usd(10'000'000)));
553 env.close();
554
555 // Lambda that:
556 // 1. Exercises one offer trio,
557 // 2. Collects the results, and
558 // 3. Cleans up for the next offer trio.
559 auto exerciseOfferTrio = [this, &env, &alice, &bob, &carol, &usd](
560 Amounts const& carolOffer) -> unsigned int {
561 // alice submits an offer that may become a blocker.
562 std::uint32_t const aliceOfferSeq = env.seq(alice);
563 static Amounts const kAliceInitialOffer(usd(2), drops(3382562));
564 env(offer(alice, kAliceInitialOffer.in, kAliceInitialOffer.out));
565 env.close();
567 env, alice, aliceOfferSeq)[jss::node]))
568 .rate();
569
570 // bob submits an offer that is more desirable than alice's
571 std::uint32_t const bobOfferSeq = env.seq(bob);
572 env(offer(bob, usd(0.97086565812384), drops(1642020)));
573 env.close();
574
575 // Now carol's offer consumes bob's and partially crosses
576 // alice's. The tfSell flag is important.
577 std::uint32_t const carolOfferSeq = env.seq(carol);
578 env(offer(carol, carolOffer.in, carolOffer.out), Txflags(tfSell));
579 env.close();
580
581 // carol's offer should not have made it into the ledger and
582 // bob's offer should be fully consumed.
583 if (!BEAST_EXPECT(
584 !offerInLedger(env, carol, carolOfferSeq) &&
585 !offerInLedger(env, bob, bobOfferSeq)))
586 {
587 // If carol's or bob's offers are still in the ledger then
588 // further results are meaningless.
590 env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}, {carol, carolOfferSeq}});
591 return 1;
592 }
593 // alice's offer should still be in the ledger, but reduced in
594 // size.
595 unsigned int badRate = 1;
596 {
597 json::Value aliceOffer = ledgerEntryOffer(env, alice, aliceOfferSeq);
598
599 Amounts const aliceReducedOffer = jsonOfferToAmounts(aliceOffer[jss::node]);
600
601 BEAST_EXPECT(aliceReducedOffer.in < kAliceInitialOffer.in);
602 BEAST_EXPECT(aliceReducedOffer.out < kAliceInitialOffer.out);
603 STAmount const inLedgerRate = Quality(aliceReducedOffer).rate();
604 badRate = inLedgerRate > initialRate ? 1 : 0;
605
606 // If the inLedgerRate is less than initial rate, then
607 // incrementing the mantissa of the reduced TakerGets
608 // should result in a rate higher than initial. Check
609 // this to verify that the largest allowable TakerGets
610 // was computed.
611 if (badRate == 0)
612 {
613 STAmount const tweakedTakerGets(
614 aliceReducedOffer.in.asset(),
615 aliceReducedOffer.in.mantissa() + 1,
616 aliceReducedOffer.in.exponent(),
617 aliceReducedOffer.in.negative());
618 STAmount const tweakedRate =
619 Quality(Amounts{aliceReducedOffer.in, tweakedTakerGets}).rate();
620 BEAST_EXPECT(tweakedRate > initialRate);
621 }
622#if 0
623 std::cout << "Placed rate: " << initialRate
624 << "; in-ledger rate: " << inLedgerRate
625 << "; TakerPays: " << aliceReducedOffer.in
626 << "; TakerGets: " << aliceReducedOffer.out
627 << std::endl;
628// #else
629 std::string_view filler = badRate ? "**" : " ";
630 std::cout << "| " << aliceReducedOffer.in << "` | `"
631 << aliceReducedOffer.out << "` | `" << initialRate
632 << "` | " << filler << "`" << inLedgerRate << "`"
633 << filler << std::endl;
634#endif
635 }
636
637 // In preparation for the next iteration make sure all three
638 // offers are gone from the ledger.
640 env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}, {carol, carolOfferSeq}});
641 return badRate;
642 };
643
644 static constexpr int kLoopCount = 100;
645 unsigned int blockedCount = 0;
646 {
647 STAmount increaseGets = usd(0);
648 STAmount const step(increaseGets.asset(), 1, -8);
649 for (unsigned int i = 0; i < kLoopCount; ++i)
650 {
651 blockedCount +=
652 exerciseOfferTrio(Amounts(drops(1642020), usd(1) + increaseGets));
653 increaseGets += step;
654 }
655 }
656
657 // If fixReducedOffersV2 is enabled, then none of the test cases
658 // should produce a potentially blocking rate.
659 //
660 // Also verify that if fixReducedOffersV2 is not enabled then
661 // some of the test cases produced a potentially blocking rate.
662 if (features[fixReducedOffersV2])
663 {
664 BEAST_EXPECT(blockedCount == 0);
665 }
666 else
667 {
668 BEAST_EXPECT(blockedCount > 80);
669 }
670 }
671 }
672
673 void
682};
683
684BEAST_DEFINE_TESTSUITE_PRIO(ReducedOffer, app, xrpl, 2);
685
686} // namespace xrpl::test
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Represents a JSON value.
Definition json_value.h:130
bool isMember(char const *key) const
Return true if the object has a member named key.
Represents the logical ratio of output currency to input currency.
Definition Quality.h:91
STAmount rate() const
Returns the quality as STAmount.
Definition Quality.h:149
std::uint64_t mantissa() const noexcept
Definition STAmount.h:472
int signum() const noexcept
Definition STAmount.h:504
bool negative() const noexcept
Definition STAmount.h:466
Asset const & asset() const
Definition STAmount.h:478
int exponent() const noexcept
Definition STAmount.h:441
static auto ledgerEntryOffer(jtx::Env &env, jtx::Account const &acct, std::uint32_t offerSeq)
void run() override
Runs the suite.
static bool offerInLedger(jtx::Env &env, jtx::Account const &acct, std::uint32_t offerSeq)
static Amounts jsonOfferToAmounts(json::Value const &json)
static void cleanupOldOffers(jtx::Env &env, std::initializer_list< std::pair< jtx::Account const &, std::uint32_t > > list)
Immutable cryptographic account descriptor.
Definition jtx/Account.h:17
std::string const & human() const
Returns the human readable public key.
Definition jtx/Account.h:92
A transaction testing environment.
Definition Env.h:143
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:296
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:275
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:864
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:327
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:605
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:353
Set the fee on a JTx.
Definition fee.h:15
Converts to IOU Issue or STAmount.
Set the flags on a JTx.
Definition txflags.h:9
T endl(T... args)
JSON (JavaScript Object Notation).
Definition json_errors.h:5
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
json::Value offerCancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition offer.cpp:31
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
FeatureBitset testableAmendments()
Definition Env.h:76
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
OwnerCount< ltOFFER > offers
Match the number of offers in the account's owner directory.
Definition owners.h:70
json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:15
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
BEAST_DEFINE_TESTSUITE_PRIO(AccountDelete, app, xrpl, 2)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
TAmounts< STAmount, STAmount > Amounts
Definition Quality.h:64
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
STAmount amountFromJson(SField const &name, json::Value const &v)
Definition STAmount.cpp:916