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