xrpld
Loading...
Searching...
No Matches
PaymentSandbox_test.cpp
1#include <test/jtx/Account.h>
2#include <test/jtx/Env.h>
3#include <test/jtx/PathSet.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/amount.h>
6#include <test/jtx/balance.h>
7#include <test/jtx/jtx_json.h>
8#include <test/jtx/offer.h>
9#include <test/jtx/pay.h>
10#include <test/jtx/sig.h>
11#include <test/jtx/sponsor.h>
12#include <test/jtx/ter.h>
13#include <test/jtx/trust.h>
14#include <test/jtx/txflags.h>
15
16#include <xrpl/beast/unit_test/suite.h>
17#include <xrpl/beast/utility/Zero.h>
18#include <xrpl/ledger/ApplyView.h>
19#include <xrpl/ledger/ApplyViewImpl.h>
20#include <xrpl/ledger/OwnerCounts.h>
21#include <xrpl/ledger/PaymentSandbox.h>
22#include <xrpl/ledger/ReadView.h>
23#include <xrpl/ledger/helpers/RippleStateHelpers.h>
24#include <xrpl/ledger/helpers/TokenHelpers.h>
25#include <xrpl/protocol/AccountID.h>
26#include <xrpl/protocol/AmountConversions.h>
27#include <xrpl/protocol/Feature.h>
28#include <xrpl/protocol/Indexes.h>
29#include <xrpl/protocol/Issue.h>
30#include <xrpl/protocol/SField.h>
31#include <xrpl/protocol/STAmount.h>
32#include <xrpl/protocol/TER.h>
33#include <xrpl/protocol/TxFlags.h>
34#include <xrpl/protocol/UintTypes.h>
35#include <xrpl/protocol/XRPAmount.h>
36
37#include <cstdint>
38
39namespace xrpl::test {
40
42{
43 /*
44 Create paths so one path funds another path.
45
46 Two accounts: sender and receiver.
47 Two gateways: gw1 and gw2.
48 Sender and receiver both have trust lines to the gateways.
49 Sender has 2 gw1/USD and 4 gw2/USD.
50 Sender has offer to exchange 2 gw1 for gw2 and gw2 for gw1 1-for-1.
51 Paths are:
52 1) GW1 -> [OB GW1/USD->GW2/USD] -> GW2
53 2) GW2 -> [OB GW2/USD->GW1/USD] -> GW1
54
55 sender pays receiver 4 USD.
56 Path 1:
57 1) Sender exchanges 2 GW1/USD for 2 GW2/USD
58 2) Old code: the 2 GW1/USD is available to sender
59 New code: the 2 GW1/USD is not available until the
60 end of the transaction.
61 3) Receiver gets 2 GW2/USD
62 Path 2:
63 1) Old code: Sender exchanges 2 GW2/USD for 2 GW1/USD
64 2) Old code: Receiver get 2 GW1
65 2) New code: Path is dry because sender does not have any
66 GW1 to spend until the end of the transaction.
67 */
68 void
70 {
71 testcase("selfFunding");
72
73 using namespace jtx;
74 Env env(*this, features);
75 Account const gw1("gw1");
76 Account const gw2("gw2");
77 Account const snd("snd");
78 Account const rcv("rcv");
79
80 env.fund(XRP(10000), snd, rcv, gw1, gw2);
81
82 auto const usdGw1 = gw1["USD"];
83 auto const usdGw2 = gw2["USD"];
84
85 env.trust(usdGw1(10), snd);
86 env.trust(usdGw2(10), snd);
87 env.trust(usdGw1(100), rcv);
88 env.trust(usdGw2(100), rcv);
89
90 env(pay(gw1, snd, usdGw1(2)));
91 env(pay(gw2, snd, usdGw2(4)));
92
93 env(offer(snd, usdGw1(2), usdGw2(2)), Txflags(tfPassive));
94 env(offer(snd, usdGw2(2), usdGw1(2)), Txflags(tfPassive));
95
96 PathSet const paths(TestPath(gw1, usdGw2, gw2), TestPath(gw2, usdGw1, gw1));
97
98 env(pay(snd, rcv, kAny(usdGw1(4))),
99 Json(paths.json()),
100 Txflags(tfNoRippleDirect | tfPartialPayment));
101
102 env.require(Balance("rcv", usdGw1(0)));
103 env.require(Balance("rcv", usdGw2(2)));
104 }
105
106 void
108 {
109 testcase("subtractCredits");
110
111 using namespace jtx;
112 Env env(*this, features);
113 Account const gw1("gw1");
114 Account const gw2("gw2");
115 Account const alice("alice");
116
117 env.fund(XRP(10000), alice, gw1, gw2);
118
119 auto j = env.app().getJournal("View");
120
121 auto const usdGw1 = gw1["USD"];
122 auto const usdGw2 = gw2["USD"];
123
124 env.trust(usdGw1(100), alice);
125 env.trust(usdGw2(100), alice);
126
127 env(pay(gw1, alice, usdGw1(50)));
128 env(pay(gw2, alice, usdGw2(50)));
129
130 STAmount const toCredit(usdGw1(30));
131 STAmount const toDebit(usdGw1(20));
132 {
133 // accountSend, no deferredCredits
134 ApplyViewImpl av(&*env.current(), TapNone);
135
136 auto const iss = usdGw1;
137 auto const startingAmount =
138 accountHolds(av, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j);
139 {
140 auto r = accountSend(av, gw1, alice, toCredit, j);
141 BEAST_EXPECT(isTesSuccess(r));
142 }
143 BEAST_EXPECT(
145 av, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
146 startingAmount + toCredit);
147 {
148 auto r = accountSend(av, alice, gw1, toDebit, j);
149 BEAST_EXPECT(isTesSuccess(r));
150 }
151 BEAST_EXPECT(
153 av, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
154 startingAmount + toCredit - toDebit);
155 }
156
157 {
158 // directSendNoFee, no deferredCredits
159 ApplyViewImpl av(&*env.current(), TapNone);
160
161 auto const iss = usdGw1;
162 auto const startingAmount =
163 accountHolds(av, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j);
164
165 directSendNoFee(av, gw1, alice, toCredit, true, j);
166 BEAST_EXPECT(
168 av, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
169 startingAmount + toCredit);
170
171 directSendNoFee(av, alice, gw1, toDebit, true, j);
172 BEAST_EXPECT(
174 av, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
175 startingAmount + toCredit - toDebit);
176 }
177
178 {
179 // accountSend, w/ deferredCredits
180 ApplyViewImpl av(&*env.current(), TapNone);
181 PaymentSandbox pv(&av);
182
183 auto const iss = usdGw1;
184 auto const startingAmount =
185 accountHolds(pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j);
186
187 {
188 auto r = accountSend(pv, gw1, alice, toCredit, j);
189 BEAST_EXPECT(isTesSuccess(r));
190 }
191 BEAST_EXPECT(
193 pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
194 startingAmount);
195
196 {
197 auto r = accountSend(pv, alice, gw1, toDebit, j);
198 BEAST_EXPECT(isTesSuccess(r));
199 }
200 BEAST_EXPECT(
202 pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
203 startingAmount - toDebit);
204 }
205
206 {
207 // directSendNoFee, w/ deferredCredits
208 ApplyViewImpl av(&*env.current(), TapNone);
209 PaymentSandbox pv(&av);
210
211 auto const iss = usdGw1;
212 auto const startingAmount =
213 accountHolds(pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j);
214
215 directSendNoFee(pv, gw1, alice, toCredit, true, j);
216 BEAST_EXPECT(
218 pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
219 startingAmount);
220 }
221
222 {
223 // redeemIOU, w/ deferredCredits
224 ApplyViewImpl av(&*env.current(), TapNone);
225 PaymentSandbox pv(&av);
226
227 auto const iss = usdGw1;
228 auto const startingAmount =
229 accountHolds(pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j);
230
231 BEAST_EXPECT(redeemIOU(pv, alice, toDebit, iss, j) == tesSUCCESS);
232 BEAST_EXPECT(
234 pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
235 startingAmount - toDebit);
236 }
237
238 {
239 // issueIOU, w/ deferredCredits
240 ApplyViewImpl av(&*env.current(), TapNone);
241 PaymentSandbox pv(&av);
242
243 auto const iss = usdGw1;
244 auto const startingAmount =
245 accountHolds(pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j);
246
247 BEAST_EXPECT(issueIOU(pv, alice, toCredit, iss, {}, j) == tesSUCCESS);
248 BEAST_EXPECT(
250 pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
251 startingAmount);
252 }
253
254 {
255 // accountSend, w/ deferredCredits and stacked views
256 ApplyViewImpl av(&*env.current(), TapNone);
257 PaymentSandbox pv(&av);
258
259 auto const iss = usdGw1;
260 auto const startingAmount =
261 accountHolds(pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j);
262
263 {
264 auto r = accountSend(pv, gw1, alice, toCredit, j);
265 BEAST_EXPECT(isTesSuccess(r));
266 }
267 BEAST_EXPECT(
269 pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
270 startingAmount);
271
272 {
273 PaymentSandbox pv2(&pv);
274 BEAST_EXPECT(
276 pv2, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
277 startingAmount);
278 {
279 auto r = accountSend(pv2, gw1, alice, toCredit, j);
280 BEAST_EXPECT(isTesSuccess(r));
281 }
282 BEAST_EXPECT(
284 pv2, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
285 startingAmount);
286 }
287
288 {
289 auto r = accountSend(pv, alice, gw1, toDebit, j);
290 BEAST_EXPECT(isTesSuccess(r));
291 }
292 BEAST_EXPECT(
294 pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) ==
295 startingAmount - toDebit);
296 }
297 }
298
299 void
301 {
302 testcase("Tiny balance");
303
304 // Add and subtract a huge credit from a tiny balance, expect the tiny
305 // balance back. Numerical stability problems could cause the balance to
306 // be zero.
307
308 using namespace jtx;
309
310 Env const env(*this, features);
311
312 Account const gw("gw");
313 Account const alice("alice");
314 auto const usd = gw["USD"];
315
316 auto const issue = usd;
317 STAmount const tinyAmt(
319 STAmount const hugeAmt(
321
322 ApplyViewImpl av(&*env.current(), TapNone);
323 PaymentSandbox pv(&av);
324 pv.creditHookIOU(gw, alice, hugeAmt, -tinyAmt);
325 BEAST_EXPECT(pv.balanceHookIOU(alice, gw, hugeAmt) == tinyAmt);
326 }
327
328 void
330 {
331 testcase("Reserve");
332 using namespace jtx;
333
334 auto accountFundsXRP =
335 [](ReadView const& view, AccountID const& id, beast::Journal j) -> XRPAmount {
338 };
339
340 auto reserve = [](jtx::Env& env, std::uint32_t count) -> XRPAmount {
341 return env.current()->fees().accountReserve(count, 1);
342 };
343
344 Env env(*this, features);
345
346 Account const alice("alice");
347 env.fund(reserve(env, 1), alice);
348
349 env.close();
350 ApplyViewImpl av(&*env.current(), TapNone);
351 PaymentSandbox sb(&av);
352 {
353 // Send alice an amount and spend it. The deferredCredits will cause
354 // her balance to drop below the reserve. Make sure her funds are
355 // zero (there was a bug that caused her funds to become negative).
356
357 {
358 auto r = accountSend(sb, xrpAccount(), alice, XRP(100), env.journal);
359 BEAST_EXPECT(isTesSuccess(r));
360 }
361 {
362 auto r = accountSend(sb, alice, xrpAccount(), XRP(100), env.journal);
363 BEAST_EXPECT(isTesSuccess(r));
364 }
365 BEAST_EXPECT(accountFundsXRP(sb, alice, env.journal) == beast::kZero);
366 }
367 }
368
369 void
371 {
372 // Make sure the Issue::Account returned by
373 // PaymentSandbox::balanceHookIOU is correct.
374 testcase("balanceHook");
375
376 using namespace jtx;
377 Env const env(*this, features);
378
379 Account const gw("gw");
380 auto const usd = gw["USD"];
381 Account const alice("alice");
382
383 ApplyViewImpl av(&*env.current(), TapNone);
384 PaymentSandbox sb(&av);
385
386 // The currency we pass for the last argument mimics the currency that
387 // is typically passed to creditHookIOU, since it comes from a trust
388 // line.
389 Issue tlIssue = noIssue();
390 tlIssue.currency = usd.currency;
391
392 sb.creditHookIOU(gw.id(), alice.id(), {usd, 400}, {tlIssue, 600});
393 sb.creditHookIOU(gw.id(), alice.id(), {usd, 100}, {tlIssue, 600});
394
395 // Expect that the STAmount issuer returned by balanceHookIOU() is correct.
396 STAmount const balance = sb.balanceHookIOU(gw.id(), alice.id(), {usd, 600});
397 BEAST_EXPECT(balance.getIssuer() == usd.account.id());
398 }
399
400 void
402 {
403 // Test that PaymentSandbox::adjustOwnerCountHook and ownerCountHook
404 // correctly track and return the maximum owner counts during a payment.
405 testcase("ownerCountHook");
406
407 using namespace jtx;
408 Env env(*this, features);
409 Account const alice("alice");
410 Account const sponsor("sponsor");
411
412 env.fund(XRP(10000), alice, sponsor);
413 env.close();
414
415 ApplyViewImpl av(&*env.current(), TapNone);
416 PaymentSandbox sb(&av);
417
418 // Test basic owner count hook without sponsor
419 {
420 auto const aliceSle = sb.peek(keylet::account(alice));
421 BEAST_EXPECT(aliceSle);
422
423 OwnerCounts const initial(aliceSle);
424 OwnerCounts updated = initial;
425 updated.owner = initial.owner + 2;
426
427 // Simulate adjusting owner count
428 sb.adjustOwnerCountHook(alice, initial, updated);
429
430 // ownerCountHook should return the max value
431 OwnerCounts const retrieved = sb.ownerCountHook(alice, initial);
432 BEAST_EXPECT(retrieved.owner == updated.owner);
433 BEAST_EXPECT(retrieved.sponsored == updated.sponsored);
434 BEAST_EXPECT(retrieved.sponsoring == updated.sponsoring);
435 }
436
437 // Test owner count hook with sponsor-related counts
438 {
439 auto const sponsorSle = sb.peek(keylet::account(sponsor));
440 BEAST_EXPECT(sponsorSle);
441
442 OwnerCounts const sponsorInitial(sponsorSle);
443 OwnerCounts sponsorUpdated = sponsorInitial;
444 sponsorUpdated.owner = sponsorInitial.owner + 1;
445 sponsorUpdated.sponsoring = sponsorInitial.sponsoring + 1;
446
447 sb.adjustOwnerCountHook(sponsor, sponsorInitial, sponsorUpdated);
448
449 OwnerCounts const sponsorRetrieved = sb.ownerCountHook(sponsor, sponsorInitial);
450 BEAST_EXPECT(sponsorRetrieved.owner == sponsorUpdated.owner);
451 BEAST_EXPECT(sponsorRetrieved.sponsoring == sponsorUpdated.sponsoring);
452 }
453
454 // Test with stacked PaymentSandboxes
455 {
456 PaymentSandbox sb2(&sb);
457
458 auto const aliceSle = sb2.peek(keylet::account(alice));
459 OwnerCounts const current(aliceSle);
460 OwnerCounts further = current;
461 further.owner = current.owner + 3;
462
463 sb2.adjustOwnerCountHook(alice, current, further);
464
465 // The nested sandbox should see the max from both levels
466 OwnerCounts const retrieved = sb2.ownerCountHook(alice, OwnerCounts());
467 BEAST_EXPECT(retrieved.owner >= further.owner);
468 }
469
470 // Test that max logic works correctly
471 {
472 auto const aliceSle = sb.peek(keylet::account(alice));
473 OwnerCounts const current(aliceSle);
474 OwnerCounts lower = current;
475 lower.owner = (current.owner > 0) ? current.owner - 1 : 0;
476
477 // Adjusting to a lower value
478 sb.adjustOwnerCountHook(alice, current, lower);
479
480 // Should still return the higher value seen previously
481 OwnerCounts const retrieved = sb.ownerCountHook(alice, OwnerCounts());
482 BEAST_EXPECT(retrieved.owner >= lower.owner);
483 }
484 }
485
486 void
488 {
489 // Test that owner count hooks work correctly during actual transactions.
490 // This verifies that when transactions modify owner counts (by creating
491 // or deleting ledger objects), the hooks properly track these changes.
492 testcase(
493 std::string("ownerCountWithTransaction") +
494 (features[featureSponsor] ? " with sponsor" : " without sponsor"));
495
496 using namespace jtx;
497
498 auto reserve = [](jtx::Env& env, std::uint32_t count) -> XRPAmount {
499 return env.current()->fees().accountReserve(count, 1);
500 };
501
502 Env env(*this, features);
503 Account const gw("gw");
504 Account const alice("alice");
505 Account const bob("bob");
506 Account const sponsor("sponsor");
507
508 auto const usd = gw["USD"];
509
510 // Fund accounts. Alice starts with exactly enough for base reserve + 2 objects
511 env.fund(XRP(10000), gw, bob, sponsor);
512 env.fund(reserve(env, 3) + XRP(100), alice); // Base + 2 objects + extra for fees
513 env.close();
514
515 // Verify initial state - no owner count
516 BEAST_EXPECT(ownerCount(env, alice) == 0);
517 BEAST_EXPECT(ownerCount(env, bob) == 0);
518
519 // Create a trust line - this increases owner count
520 env(trust(alice, usd(1000)));
521 env.close();
522
523 // alice now has 1 object (owner count = 1)
524 BEAST_EXPECT(ownerCount(env, alice) == 1);
525
526 // Create an offer - this further increases owner count
527 env(trust(bob, usd(1000)));
528 env(pay(gw, alice, usd(100)));
529 env.close();
530
531 auto const aliceOfferSeq = env.seq(alice); // Capture the sequence before creating offer
532 env(offer(alice, usd(50), XRP(50)));
533 env.close();
534
535 // alice now has 2 objects (trust line + offer)
536 BEAST_EXPECT(ownerCount(env, alice) == 2);
537
538 // If sponsor feature is enabled, test sponsorship transfer
539 if (features[featureSponsor])
540 {
541 auto const trustId = keylet::trustLine(alice, gw, usd.currency);
542 BEAST_EXPECT(env.le(trustId));
543
544 // Transfer sponsorship - sponsor now sponsors alice's trust line
545 env(sponsor::transfer(alice, tfSponsorshipCreate, trustId.key),
547 Sig(sfSponsorSignature, sponsor));
548 env.close();
549
550 // alice still has 2 objects but 1 is sponsored
551 BEAST_EXPECT(ownerCount(env, alice) == 2);
552 BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
553 // sponsor's sponsoring count should increase
554 BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
555 }
556
557 // Verify alice's available balance respects the reserve
558 auto const aliceSle = env.le(keylet::account(alice));
559 BEAST_EXPECT(aliceSle);
560
561 auto const aliceBalance = aliceSle->getFieldAmount(sfBalance);
562 // With sponsor, 1 object is sponsored so only 1 counts for reserve
563 auto const aliceReserve = reserve(env, features[featureSponsor] ? 1 : 2);
564
565 // alice should have limited available balance after accounting for reserve
566 auto const available = aliceBalance.xrp() - aliceReserve;
567 if (features[featureSponsor])
568 {
569 // With sponsor, alice has more available (1 sponsored object = less reserve)
570 BEAST_EXPECT(available > XRP(150));
571 }
572 else
573 {
574 BEAST_EXPECT(available < XRP(150)); // Most of the balance is in reserve
575 }
576
577 // Try to send nearly all balance - should fail due to reserve in both cases
578 auto const tooMuch = aliceBalance.xrp() - XRP(1);
579 env(pay(alice, bob, tooMuch), Ter(tecUNFUNDED_PAYMENT));
580 env.close();
581
582 // Verify owner count hasn't changed
583 BEAST_EXPECT(ownerCount(env, alice) == 2);
584
585 // Cancel the offer - this decreases owner count
586 env(offerCancel(alice, aliceOfferSeq));
587 env.close();
588
589 // alice now has 1 object (just the trust line)
590 BEAST_EXPECT(ownerCount(env, alice) == 1);
591
592 if (features[featureSponsor])
593 {
594 // Verify sponsored count stayed the same (trust line is still sponsored)
595 BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
596 BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
597 }
598
599 // Now alice should have more available balance (less reserve needed)
600 auto const aliceSle2 = env.le(keylet::account(alice));
601 auto const aliceBalance2 = aliceSle2->getFieldAmount(sfBalance);
602 // With sponsor, trust line is still sponsored so 0 objects for reserve
603 // Without sponsor, 1 object for reserve
604 auto const aliceReserve2 = reserve(env, features[featureSponsor] ? 0 : 1);
605 auto const available2 = aliceBalance2.xrp() - aliceReserve2;
606
607 // available2 should be greater than available (less reserve needed)
608 BEAST_EXPECT(available2 > available);
609 }
610
611public:
612 void
613 run() override
614 {
615 auto testAll = [this](FeatureBitset features) {
616 testSelfFunding(features);
617 testSubtractCredits(features);
618 testTinyBalance(features);
619 testReserve(features);
620 testBalanceHook(features);
621 testOwnerCountHook(features);
622 };
623 using namespace jtx;
624 auto const sa = testableAmendments();
625 testAll(sa - featurePermissionedDEX);
626 testAll(sa);
627
628 // Test owner count with transactions
629 testOwnerCountWithTransaction(sa - featureSponsor);
631 }
632};
633
635
636} // namespace xrpl::test
A generic endpoint for log messages.
Definition Journal.h:44
A testsuite class.
Definition suite.h:52
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Editable, discardable view that can build metadata for one tx.
A currency issued by an account.
Definition Issue.h:18
Currency currency
Definition Issue.h:20
A wrapper which makes credits unavailable to balances.
STAmount balanceHookIOU(AccountID const &account, AccountID const &issuer, STAmount const &amount) const override
void creditHookIOU(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance) override
void adjustOwnerCountHook(AccountID const &account, OwnerCounts const &cur, OwnerCounts const &next) override
OwnerCounts ownerCountHook(AccountID const &account, OwnerCounts const &count) const override
A view into a ledger.
Definition ReadView.h:41
static constexpr int kMinOffset
Definition STAmount.h:61
AccountID const & getIssuer() const
Definition STAmount.h:516
static constexpr std::uint64_t kMinValue
Definition STAmount.h:65
static constexpr std::uint64_t kMaxValue
Definition STAmount.h:67
static constexpr int kMaxOffset
Definition STAmount.h:62
virtual beast::Journal getJournal(std::string const &name)=0
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
json::Value json() const
Definition PathSet.h:183
void testBalanceHook(FeatureBitset features)
void run() override
Runs the suite.
void testOwnerCountHook(FeatureBitset features)
void testSubtractCredits(FeatureBitset features)
void testOwnerCountWithTransaction(FeatureBitset features)
void testReserve(FeatureBitset features)
void testTinyBalance(FeatureBitset features)
void testSelfFunding(FeatureBitset features)
Immutable cryptographic account descriptor.
Definition jtx/Account.h:21
AccountID id() const
Returns the Account ID.
A transaction testing environment.
Definition Env.h:161
Application & app()
Definition Env.h:300
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
SLE::const_pointer le(Account const &account) const
Return an account root.
Definition Env.cpp:311
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:323
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:302
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:354
beast::Journal const journal
Definition Env.h:204
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
Inject raw JSON.
Definition jtx_json.h:16
Set the regular signature on a JTx.
Definition sig.h:19
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:18
Set the flags on a JTx.
Definition txflags.h:14
constexpr Zero kZero
Definition Zero.h:30
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:253
json::Value transfer(jtx::Account const &account, uint32_t flags, std::optional< uint256 > const &index)
Definition sponsor.cpp:52
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
std::uint32_t ownerCount(Env const &env, Account const &account)
std::uint32_t sponsoringOwnerCount(Env const &env, Account const &account)
FeatureBitset testableAmendments()
Definition Env.h:92
AnyT const kAny
Returns an amount representing "any issuer".
Definition amount.cpp:120
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:14
std::uint32_t sponsoredOwnerCount(Env const &env, Account const &account)
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:18
static XRPAmount reserve(jtx::Env &env, std::uint32_t count)
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
constexpr XRPAmount
Convert XRP to drops (integral types).
Definition TxTest.h:54
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, SLE::ref sponsorSle={}, WaiveTransferFee waiveFee=WaiveTransferFee::No, AllowMPTOverflow allowOverflow=AllowMPTOverflow::No)
Calls static accountSendIOU if saAmount represents Issue.
Currency const & xrpCurrency()
XRP currency.
Definition UintTypes.cpp:99
XRPAmount toAmount< XRPAmount >(STAmount const &amt)
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, SLE::ref sponsorSle, beast::Journal j)
TER directSendNoFee(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static directSendNoFeeIOU if saAmount represents Issue.
@ TapNone
Definition ApplyView.h:28
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
bool isTesSuccess(TER x) noexcept
Definition TER.h:676
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:118
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecUNFUNDED_PAYMENT
Definition TER.h:288
constexpr FlagValue spfSponsorReserve
Definition TxFlags.h:460
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=SpendableHandling::SimpleBalance)
@ tesSUCCESS
Definition TER.h:245
std::uint32_t sponsored
Definition OwnerCounts.h:17
std::uint32_t sponsoring
Definition OwnerCounts.h:18
std::uint32_t owner
Definition OwnerCounts.h:16