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