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 }
148
149 // In preparation for the next iteration make sure the two
150 // offers are gone from the ledger.
151 cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
152 return badRate;
153 };
154
155 // bob's offer (the new offer) is the same every time:
156 Amounts const bobOffer{STAmount(XRP(1)), STAmount(usd, 1, 0)};
157
158 // alice's offer has a slightly smaller TakerPays with each
159 // iteration. This should mean that the size of the offer bob
160 // places in the ledger should increase with each iteration.
161 unsigned int blockedCount = 0;
162 for (std::uint64_t mantissaReduce = 1'000'000'000ull;
163 mantissaReduce <= 5'000'000'000ull;
164 mantissaReduce += 20'000'000ull)
165 {
166 STAmount const aliceUSD{
167 bobOffer.out.asset(),
168 bobOffer.out.mantissa() - mantissaReduce,
169 bobOffer.out.exponent()};
170 STAmount const aliceXRP{bobOffer.in.asset(), bobOffer.in.mantissa() - 1};
171 Amounts const aliceOffer{aliceUSD, aliceXRP};
172 blockedCount += exerciseOfferPair(aliceOffer, bobOffer);
173 }
174
175 // None of the test cases should produce a potentially blocking
176 // rate.
177 BEAST_EXPECT(blockedCount == 0);
178 }
179 }
180
181 void
183 {
184 testcase("exercise partial cross old XRP/IOU offer Q change");
185
186 using namespace jtx;
187
188 auto const gw = Account{"gateway"};
189 auto const alice = Account{"alice"};
190 auto const bob = Account{"bob"};
191 auto const usd = gw["USD"];
192
193 {
194 // Make sure none of the offers we generate are under funded.
195 Env env{*this, testableAmendments()};
196 env.fund(XRP(10'000'000), gw, alice, bob);
197 env.close();
198
199 env(trust(alice, usd(10'000'000)));
200 env(trust(bob, usd(10'000'000)));
201 env.close();
202
203 env(pay(gw, alice, usd(10'000'000)));
204 env.close();
205
206 // Lambda that:
207 // 1. Exercises one offer pair,
208 // 2. Collects the results, and
209 // 3. Cleans up for the next offer pair.
210 auto exerciseOfferPair = [this, &env, &alice, &bob](
211 Amounts const& inLedger,
212 Amounts const& newOffer) -> unsigned int {
213 // Get the inLedger offer into the ledger so newOffer can cross
214 // it.
215 STAmount const initialRate = Quality(inLedger).rate();
216 std::uint32_t const aliceOfferSeq = env.seq(alice);
217 env(offer(alice, inLedger.in, inLedger.out));
218 env.close();
219
220 // Now bob's offer will partially cross alice's offer.
221 std::uint32_t const bobOfferSeq = env.seq(bob);
222 STAmount const aliceInitialBalance = env.balance(alice);
223 env(offer(bob, newOffer.in, newOffer.out));
224 env.close();
225 STAmount const aliceFinalBalance = env.balance(alice);
226
227 // bob's offer should not have made it into the ledger.
228 if (!BEAST_EXPECT(!offerInLedger(env, bob, bobOfferSeq)))
229 {
230 // If the in-ledger offer was not consumed then further
231 // results are meaningless.
232 cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
233 return 1;
234 }
235 // alice's offer should still be in the ledger, but reduced in
236 // size.
237 unsigned int badRate = 1;
238 {
239 json::Value aliceOffer = ledgerEntryOffer(env, alice, aliceOfferSeq);
240
241 STAmount const reducedTakerGets =
242 amountFromJson(sfTakerGets, aliceOffer[jss::node][sfTakerGets.jsonName]);
243 STAmount const reducedTakerPays =
244 amountFromJson(sfTakerPays, aliceOffer[jss::node][sfTakerPays.jsonName]);
245 STAmount const aliceGot = env.balance(alice) - aliceInitialBalance;
246 BEAST_EXPECT(reducedTakerPays < inLedger.in);
247 BEAST_EXPECT(reducedTakerGets < inLedger.out);
248 STAmount const inLedgerRate =
249 Quality(Amounts{reducedTakerPays, reducedTakerGets}).rate();
250 badRate = inLedgerRate > initialRate ? 1 : 0;
251
252 // If the inLedgerRate is less than initial rate, then
253 // incrementing the mantissa of the reduced taker pays
254 // should result in a rate higher than initial. Check
255 // this to verify that the largest allowable TakerPays
256 // was computed.
257 if (badRate == 0)
258 {
259 STAmount const tweakedTakerPays = reducedTakerPays + drops(1);
260 STAmount const tweakedRate =
261 Quality(Amounts{tweakedTakerPays, reducedTakerGets}).rate();
262 BEAST_EXPECT(tweakedRate > initialRate);
263 }
264 }
265
266 // In preparation for the next iteration make sure the two
267 // offers are gone from the ledger.
268 cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
269 return badRate;
270 };
271
272 // alice's offer (the old offer) is the same every time:
273 Amounts const aliceOffer{STAmount(XRP(1)), STAmount(usd, 1, 0)};
274
275 // bob's offer has a slightly smaller TakerPays with each iteration.
276 // This should mean that the size of the offer alice leaves in the
277 // ledger should increase with each iteration.
278 unsigned int blockedCount = 0;
279 for (std::uint64_t mantissaReduce = 1'000'000'000ull;
280 mantissaReduce <= 4'000'000'000ull;
281 mantissaReduce += 20'000'000ull)
282 {
283 STAmount const bobUSD{
284 aliceOffer.out.asset(),
285 aliceOffer.out.mantissa() - mantissaReduce,
286 aliceOffer.out.exponent()};
287 STAmount const bobXRP{aliceOffer.in.asset(), aliceOffer.in.mantissa() - 1};
288 Amounts const bobOffer{bobUSD, bobXRP};
289
290 blockedCount += exerciseOfferPair(aliceOffer, bobOffer);
291 }
292
293 // None of the test cases should produce a potentially blocking
294 // rate.
295 BEAST_EXPECT(blockedCount == 0);
296 }
297 }
298
299 void
301 {
302 testcase("exercise underfunded XRP/IOU offer Q change");
303
304 // Bob places an offer that is not fully funded.
305
306 using namespace jtx;
307 auto const alice = Account{"alice"};
308 auto const bob = Account{"bob"};
309 auto const gw = Account{"gw"};
310 auto const usd = gw["USD"];
311
312 {
313 Env env{*this, testableAmendments()};
314
315 env.fund(XRP(10000), alice, bob, gw);
316 env.close();
317 env.trust(usd(1000), alice, bob);
318
319 int blockedOrderBookCount = 0;
320 for (STAmount initialBobUSD = usd(0.45); initialBobUSD <= usd(1);
321 initialBobUSD += usd(0.025))
322 {
323 // underfund bob's offer
324 env(pay(gw, bob, initialBobUSD));
325 env.close();
326
327 std::uint32_t const bobOfferSeq = env.seq(bob);
328 env(offer(bob, drops(2), usd(1)));
329 env.close();
330
331 // alice places an offer that would cross bob's if bob's were
332 // well funded.
333 std::uint32_t const aliceOfferSeq = env.seq(alice);
334 env(offer(alice, usd(1), drops(2)));
335 env.close();
336
337 // We want to detect order book blocking. If:
338 // 1. bob's offer is still in the ledger and
339 // 2. alice received no USD
340 // then we use that as evidence that bob's offer blocked the
341 // order book.
342 {
343 bool const bobOfferGone = !offerInLedger(env, bob, bobOfferSeq);
344 STAmount const aliceBalanceUSD = env.balance(alice, usd);
345
346 // Sanity check the ledger if alice got USD.
347 if (aliceBalanceUSD.signum() > 0)
348 {
349 BEAST_EXPECT(aliceBalanceUSD == initialBobUSD);
350 BEAST_EXPECT(env.balance(bob, usd) == usd(0));
351 BEAST_EXPECT(bobOfferGone);
352 }
353
354 // Track occurrences of order book blocking.
355 if (!bobOfferGone && aliceBalanceUSD.signum() == 0)
356 {
357 ++blockedOrderBookCount;
358 }
359
360 // In preparation for the next iteration clean up any
361 // leftover offers.
362 cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
363
364 // Zero out alice's and bob's USD balances.
365 if (STAmount const aliceBalance = env.balance(alice, usd);
366 aliceBalance.signum() > 0)
367 env(pay(alice, gw, aliceBalance));
368
369 if (STAmount const bobBalance = env.balance(bob, usd); bobBalance.signum() > 0)
370 env(pay(bob, gw, bobBalance));
371
372 env.close();
373 }
374 }
375
376 // None of the test cases should produce a potentially blocking
377 // rate.
378 BEAST_EXPECT(blockedOrderBookCount == 0);
379 }
380 }
381
382 void
384 {
385 testcase("exercise underfunded IOU/IOU offer Q change");
386
387 // Bob places an IOU/IOU offer that is not fully funded.
388
389 using namespace jtx;
390 using namespace std::chrono_literals;
391 auto const alice = Account{"alice"};
392 auto const bob = Account{"bob"};
393 auto const gw = Account{"gw"};
394
395 auto const usd = gw["USD"];
396 auto const eur = gw["EUR"];
397
398 STAmount const tinyUSD(usd, /*mantissa*/ 1, /*exponent*/ -81);
399
400 {
401 Env env{*this, testableAmendments()};
402
403 env.fund(XRP(10000), alice, bob, gw);
404 env.close();
405 env.trust(usd(1000), alice, bob);
406 env.trust(eur(1000), alice, bob);
407
408 STAmount const eurOffer(eur, /*mantissa*/ 2957, /*exponent*/ -76);
409 STAmount const usdOffer(usd, /*mantissa*/ 7109, /*exponent*/ -76);
410
411 STAmount const endLoop(usd, /*mantissa*/ 50, /*exponent*/ -81);
412
413 int blockedOrderBookCount = 0;
414 for (STAmount initialBobUSD = tinyUSD; initialBobUSD <= endLoop;
415 initialBobUSD += tinyUSD)
416 {
417 // underfund bob's offer
418 env(pay(gw, bob, initialBobUSD));
419 env(pay(gw, alice, eur(100)));
420 env.close();
421
422 // This offer is underfunded
423 std::uint32_t bobOfferSeq = env.seq(bob);
424 env(offer(bob, eurOffer, usdOffer));
425 env.close();
426 env.require(offers(bob, 1));
427
428 // alice places an offer that crosses bob's.
429 std::uint32_t aliceOfferSeq = env.seq(alice);
430 env(offer(alice, usdOffer, eurOffer));
431 env.close();
432
433 // Examine the aftermath of alice's offer.
434 {
435 bool const bobOfferGone = !offerInLedger(env, bob, bobOfferSeq);
436 STAmount const aliceBalanceUSD = env.balance(alice, usd);
437 // Sanity check the ledger if alice got USD.
438 if (aliceBalanceUSD.signum() > 0)
439 {
440 BEAST_EXPECT(aliceBalanceUSD == initialBobUSD);
441 BEAST_EXPECT(env.balance(bob, usd) == usd(0));
442 BEAST_EXPECT(bobOfferGone);
443 }
444
445 // Track occurrences of order book blocking.
446 if (!bobOfferGone && aliceBalanceUSD.signum() == 0)
447 {
448 ++blockedOrderBookCount;
449 }
450 }
451
452 // In preparation for the next iteration clean up any
453 // leftover offers.
454 cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
455
456 // Zero out alice's and bob's IOU balances.
457 auto zeroBalance = [&env, &gw](Account const& acct, IOU const& iou) {
458 if (STAmount const balance = env.balance(acct, iou); balance.signum() > 0)
459 env(pay(acct, gw, balance));
460 };
461
462 zeroBalance(alice, eur);
463 zeroBalance(alice, usd);
464 zeroBalance(bob, eur);
465 zeroBalance(bob, usd);
466 env.close();
467 }
468
469 // None of the test cases should produce a potentially blocking
470 // rate.
471 BEAST_EXPECT(blockedOrderBookCount == 0);
472 }
473 }
474
475 static Amounts
477 {
478 STAmount const in = amountFromJson(sfTakerPays, json[sfTakerPays.jsonName]);
479 STAmount const out = amountFromJson(sfTakerGets, json[sfTakerGets.jsonName]);
480 return {in, out};
481 }
482
483 void
485 {
486 // This test case was motivated by Issue #4937. It recreates
487 // the specific failure identified in that issue and samples some other
488 // cases in the same vicinity to make sure that the new behavior makes
489 // sense.
490 testcase("exercise tfSell partial cross old XRP/IOU offer Q change");
491
492 using namespace jtx;
493
494 Account const gw("gateway");
495 Account const alice("alice");
496 Account const bob("bob");
497 Account const carol("carol");
498 auto const usd = gw["USD"];
499
500 // Make one test run without fixReducedOffersV2 and one with.
501 for (FeatureBitset features :
502 {testableAmendments() - fixReducedOffersV2, testableAmendments() | fixReducedOffersV2})
503 {
504 // Make sure none of the offers we generate are under funded.
505 Env env{*this, features};
506 env.fund(XRP(10'000'000), gw, alice, bob, carol);
507 env.close();
508
509 env(trust(alice, usd(10'000'000)));
510 env(trust(bob, usd(10'000'000)));
511 env(trust(carol, usd(10'000'000)));
512 env.close();
513
514 env(pay(gw, alice, usd(10'000'000)));
515 env(pay(gw, bob, usd(10'000'000)));
516 env(pay(gw, carol, usd(10'000'000)));
517 env.close();
518
519 // Lambda that:
520 // 1. Exercises one offer trio,
521 // 2. Collects the results, and
522 // 3. Cleans up for the next offer trio.
523 auto exerciseOfferTrio = [this, &env, &alice, &bob, &carol, &usd](
524 Amounts const& carolOffer) -> unsigned int {
525 // alice submits an offer that may become a blocker.
526 std::uint32_t const aliceOfferSeq = env.seq(alice);
527 static Amounts const kAliceInitialOffer(usd(2), drops(3382562));
528 env(offer(alice, kAliceInitialOffer.in, kAliceInitialOffer.out));
529 env.close();
531 env, alice, aliceOfferSeq)[jss::node]))
532 .rate();
533
534 // bob submits an offer that is more desirable than alice's
535 std::uint32_t const bobOfferSeq = env.seq(bob);
536 env(offer(bob, usd(0.97086565812384), drops(1642020)));
537 env.close();
538
539 // Now carol's offer consumes bob's and partially crosses
540 // alice's. The tfSell flag is important.
541 std::uint32_t const carolOfferSeq = env.seq(carol);
542 env(offer(carol, carolOffer.in, carolOffer.out), Txflags(tfSell));
543 env.close();
544
545 // carol's offer should not have made it into the ledger and
546 // bob's offer should be fully consumed.
547 if (!BEAST_EXPECT(
548 !offerInLedger(env, carol, carolOfferSeq) &&
549 !offerInLedger(env, bob, bobOfferSeq)))
550 {
551 // If carol's or bob's offers are still in the ledger then
552 // further results are meaningless.
554 env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}, {carol, carolOfferSeq}});
555 return 1;
556 }
557 // alice's offer should still be in the ledger, but reduced in
558 // size.
559 unsigned int badRate = 1;
560 {
561 json::Value aliceOffer = ledgerEntryOffer(env, alice, aliceOfferSeq);
562
563 Amounts const aliceReducedOffer = jsonOfferToAmounts(aliceOffer[jss::node]);
564
565 BEAST_EXPECT(aliceReducedOffer.in < kAliceInitialOffer.in);
566 BEAST_EXPECT(aliceReducedOffer.out < kAliceInitialOffer.out);
567 STAmount const inLedgerRate = Quality(aliceReducedOffer).rate();
568 badRate = inLedgerRate > initialRate ? 1 : 0;
569
570 // If the inLedgerRate is less than initial rate, then
571 // incrementing the mantissa of the reduced TakerGets
572 // should result in a rate higher than initial. Check
573 // this to verify that the largest allowable TakerGets
574 // was computed.
575 if (badRate == 0)
576 {
577 STAmount const tweakedTakerGets(
578 aliceReducedOffer.in.asset(),
579 aliceReducedOffer.in.mantissa() + 1,
580 aliceReducedOffer.in.exponent(),
581 aliceReducedOffer.in.negative());
582 STAmount const tweakedRate =
583 Quality(Amounts{aliceReducedOffer.in, tweakedTakerGets}).rate();
584 BEAST_EXPECT(tweakedRate > initialRate);
585 }
586 }
587
588 // In preparation for the next iteration make sure all three
589 // offers are gone from the ledger.
591 env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}, {carol, carolOfferSeq}});
592 return badRate;
593 };
594
595 static constexpr int kLoopCount = 100;
596 unsigned int blockedCount = 0;
597 {
598 STAmount increaseGets = usd(0);
599 STAmount const step(increaseGets.asset(), 1, -8);
600 for (unsigned int i = 0; i < kLoopCount; ++i)
601 {
602 blockedCount +=
603 exerciseOfferTrio(Amounts(drops(1642020), usd(1) + increaseGets));
604 increaseGets += step;
605 }
606 }
607
608 // If fixReducedOffersV2 is enabled, then none of the test cases
609 // should produce a potentially blocking rate.
610 //
611 // Also verify that if fixReducedOffersV2 is not enabled then
612 // some of the test cases produced a potentially blocking rate.
613 if (features[fixReducedOffersV2])
614 {
615 BEAST_EXPECT(blockedCount == 0);
616 }
617 else
618 {
619 BEAST_EXPECT(blockedCount > 80);
620 }
621 }
622 }
623
624 void
633};
634
635BEAST_DEFINE_TESTSUITE_PRIO(ReducedOffer, app, xrpl, 2);
636
637} // namespace xrpl::test
A testsuite class.
Definition suite.h:52
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Represents a JSON value.
Definition json_value.h:117
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:90
STAmount rate() const
Returns the quality as STAmount.
Definition Quality.h:162
std::uint64_t mantissa() const noexcept
Definition STAmount.h:490
int signum() const noexcept
Definition STAmount.h:522
bool negative() const noexcept
Definition STAmount.h:484
Asset const & asset() const
Definition STAmount.h:496
int exponent() const noexcept
Definition STAmount.h:459
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:21
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
Definition Env.h:161
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:323
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:302
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:1056
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
Set the fee on a JTx.
Definition fee.h:20
Converts to IOU Issue or STAmount.
Set the flags on a JTx.
Definition txflags.h:14
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:92
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:137
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_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:69
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
STAmount amountFromJson(SField const &name, json::Value const &v)
Definition STAmount.cpp:916