xrpld
Loading...
Searching...
No Matches
Clawback_test.cpp
1#include <test/jtx/Account.h>
2#include <test/jtx/Env.h>
3#include <test/jtx/TestHelpers.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/balance.h>
6#include <test/jtx/flags.h>
7#include <test/jtx/pay.h>
8#include <test/jtx/ter.h>
9#include <test/jtx/ticket.h>
10#include <test/jtx/trust.h>
11#include <test/jtx/txflags.h>
12
13#include <xrpl/basics/contract.h>
14#include <xrpl/beast/unit_test/suite.h>
15#include <xrpl/protocol/Feature.h>
16#include <xrpl/protocol/Indexes.h>
17#include <xrpl/protocol/LedgerFormats.h>
18#include <xrpl/protocol/SField.h>
19#include <xrpl/protocol/TER.h>
20#include <xrpl/protocol/TxFlags.h>
21#include <xrpl/protocol/UintTypes.h>
22
23#include <cstdint>
24#include <stdexcept>
25#include <string>
26
27namespace xrpl {
28
30{
31 template <class T>
32 static std::string
33 toString(T const& t)
34 {
35 return boost::lexical_cast<std::string>(t);
36 }
37
38 // Helper function that returns the number of tickets held by an account.
39 static std::uint32_t
41 {
42 std::uint32_t ret{0};
43 if (auto const sleAcct = env.le(acct))
44 ret = sleAcct->at(~sfTicketCount).value_or(0);
45 return ret;
46 }
47
48 // Helper function that returns the freeze status of a trustline
49 static bool
51 test::jtx::Env const& env,
52 test::jtx::Account const& src,
53 test::jtx::Account const& dst,
54 Currency const& cur)
55 {
56 if (auto sle = env.le(keylet::trustLine(src, dst, cur)))
57 {
58 auto const useHigh = src.id() > dst.id();
59 return sle->isFlag(useHigh ? lsfHighFreeze : lsfLowFreeze);
60 }
61 Throw<std::runtime_error>("No line in getLineFreezeFlag");
62 return false; // silence warning
63 }
64
65 void
67 {
68 testcase("Enable AllowTrustLineClawback flag");
69 using namespace test::jtx;
70
71 // Test that one can successfully set asfAllowTrustLineClawback flag.
72 // If successful, asfNoFreeze can no longer be set.
73 // Also, asfAllowTrustLineClawback cannot be cleared.
74 {
75 Env env(*this, features);
76 Account const alice{"alice"};
77
78 env.fund(XRP(1000), alice);
79 env.close();
80
81 // set asfAllowTrustLineClawback
82 env(fset(alice, asfAllowTrustLineClawback));
83 env.close();
84 env.require(Flags(alice, asfAllowTrustLineClawback));
85
86 // clear asfAllowTrustLineClawback does nothing
87 env(fclear(alice, asfAllowTrustLineClawback));
88 env.close();
89 env.require(Flags(alice, asfAllowTrustLineClawback));
90
91 // asfNoFreeze cannot be set when asfAllowTrustLineClawback is set
92 env.require(Nflags(alice, asfNoFreeze));
93 env(fset(alice, asfNoFreeze), Ter(tecNO_PERMISSION));
94 env.close();
95 }
96
97 // Test that asfAllowTrustLineClawback cannot be set when
98 // asfNoFreeze has been set
99 {
100 Env env(*this, features);
101 Account const alice{"alice"};
102
103 env.fund(XRP(1000), alice);
104 env.close();
105
106 env.require(Nflags(alice, asfNoFreeze));
107
108 // set asfNoFreeze
109 env(fset(alice, asfNoFreeze));
110 env.close();
111
112 // NoFreeze is set
113 env.require(Flags(alice, asfNoFreeze));
114
115 // asfAllowTrustLineClawback cannot be set if asfNoFreeze is set
116 env(fset(alice, asfAllowTrustLineClawback), Ter(tecNO_PERMISSION));
117 env.close();
118
119 env.require(Nflags(alice, asfAllowTrustLineClawback));
120 }
121
122 // Test that asfAllowTrustLineClawback is not allowed when owner dir is
123 // non-empty
124 {
125 Env env(*this, features);
126
127 Account const alice{"alice"};
128 Account const bob{"bob"};
129
130 env.fund(XRP(1000), alice, bob);
131 env.close();
132
133 auto const usd = alice["USD"];
134 env.require(Nflags(alice, asfAllowTrustLineClawback));
135
136 // alice issues 10 USD to bob
137 env.trust(usd(1000), bob);
138 env(pay(alice, bob, usd(10)));
139 env.close();
140
141 BEAST_EXPECT(ownerCount(env, alice) == 0);
142 BEAST_EXPECT(ownerCount(env, bob) == 1);
143
144 // alice fails to enable clawback because she has trustline with bob
145 env(fset(alice, asfAllowTrustLineClawback), Ter(tecOWNERS));
146 env.close();
147
148 // bob sets trustline to default limit and pays alice back to delete
149 // the trustline
150 env(trust(bob, usd(0), 0));
151 env(pay(bob, alice, usd(10)));
152
153 BEAST_EXPECT(ownerCount(env, alice) == 0);
154 BEAST_EXPECT(ownerCount(env, bob) == 0);
155
156 // alice now is able to set asfAllowTrustLineClawback
157 env(fset(alice, asfAllowTrustLineClawback));
158 env.close();
159 env.require(Flags(alice, asfAllowTrustLineClawback));
160
161 BEAST_EXPECT(ownerCount(env, alice) == 0);
162 BEAST_EXPECT(ownerCount(env, bob) == 0);
163 }
164 }
165
166 void
168 {
169 testcase("Validation");
170 using namespace test::jtx;
171
172 // Test that Clawback tx fails for the following:
173 // 1. invalid flag
174 // 2. negative STAmount
175 // 3. zero STAmount
176 // 4. XRP amount
177 // 5. `account` and `issuer` fields are same account
178 // 6. trustline has a balance of 0
179 // 7. trustline does not exist
180 {
181 Env env(*this, features);
182
183 Account const alice{"alice"};
184 Account const bob{"bob"};
185
186 env.fund(XRP(1000), alice, bob);
187 env.close();
188
189 // alice sets asfAllowTrustLineClawback
190 env(fset(alice, asfAllowTrustLineClawback));
191 env.close();
192 env.require(Flags(alice, asfAllowTrustLineClawback));
193
194 auto const usd = alice["USD"];
195
196 // alice issues 10 USD to bob
197 env.trust(usd(1000), bob);
198 env(pay(alice, bob, usd(10)));
199 env.close();
200
201 env.require(Balance(bob, alice["USD"](10)));
202 env.require(Balance(alice, bob["USD"](-10)));
203
204 // fails due to invalid flag
205 env(claw(alice, bob["USD"](5)), Txflags(0x00008000), Ter(temINVALID_FLAG));
206 env.close();
207
208 // fails due to negative amount
209 env(claw(alice, bob["USD"](-5)), Ter(temBAD_AMOUNT));
210 env.close();
211
212 // fails due to zero amount
213 env(claw(alice, bob["USD"](0)), Ter(temBAD_AMOUNT));
214 env.close();
215
216 // fails because amount is in XRP
217 env(claw(alice, XRP(10)), Ter(temBAD_AMOUNT));
218 env.close();
219
220 // fails when `issuer` field in `amount` is not token holder
221 // NOTE: we are using the `issuer` field for the token holder
222 env(claw(alice, alice["USD"](5)), Ter(temBAD_AMOUNT));
223 env.close();
224
225 // bob pays alice back, trustline has a balance of 0
226 env(pay(bob, alice, usd(10)));
227 env.close();
228
229 // bob still owns the trustline that has 0 balance
230 BEAST_EXPECT(ownerCount(env, alice) == 0);
231 BEAST_EXPECT(ownerCount(env, bob) == 1);
232 env.require(Balance(bob, alice["USD"](0)));
233 env.require(Balance(alice, bob["USD"](0)));
234
235 // clawback fails because because balance is 0
236 env(claw(alice, bob["USD"](5)), Ter(tecINSUFFICIENT_FUNDS));
237 env.close();
238
239 // set the limit to default, which should delete the trustline
240 env(trust(bob, usd(0), 0));
241 env.close();
242
243 // bob no longer owns the trustline
244 BEAST_EXPECT(ownerCount(env, alice) == 0);
245 BEAST_EXPECT(ownerCount(env, bob) == 0);
246
247 // clawback fails because trustline does not exist
248 env(claw(alice, bob["USD"](5)), Ter(tecNO_LINE));
249 env.close();
250 }
251 }
252
253 void
255 {
256 // Checks the tx submitter has the permission to clawback.
257 // Exercises preclaim code
258 testcase("Permission");
259 using namespace test::jtx;
260
261 // Clawing back from an non-existent account returns error
262 {
263 Env env(*this, features);
264
265 Account const alice{"alice"};
266 Account const bob{"bob"};
267
268 // bob's account is not funded and does not exist
269 env.fund(XRP(1000), alice);
270 env.close();
271
272 // alice sets asfAllowTrustLineClawback
273 env(fset(alice, asfAllowTrustLineClawback));
274 env.close();
275 env.require(Flags(alice, asfAllowTrustLineClawback));
276
277 // bob, the token holder, does not exist
278 env(claw(alice, bob["USD"](5)), Ter(terNO_ACCOUNT));
279 env.close();
280 }
281
282 // Test that trustline cannot be clawed by someone who is
283 // not the issuer of the currency
284 {
285 Env env(*this, features);
286
287 Account const alice{"alice"};
288 Account const bob{"bob"};
289 Account const cindy{"cindy"};
290
291 env.fund(XRP(1000), alice, bob, cindy);
292 env.close();
293
294 auto const usd = alice["USD"];
295
296 // alice sets asfAllowTrustLineClawback
297 env(fset(alice, asfAllowTrustLineClawback));
298 env.close();
299 env.require(Flags(alice, asfAllowTrustLineClawback));
300
301 // cindy sets asfAllowTrustLineClawback
302 env(fset(cindy, asfAllowTrustLineClawback));
303 env.close();
304 env.require(Flags(cindy, asfAllowTrustLineClawback));
305
306 // alice issues 1000 USD to bob
307 env.trust(usd(1000), bob);
308 env(pay(alice, bob, usd(1000)));
309 env.close();
310
311 env.require(Balance(bob, alice["USD"](1000)));
312 env.require(Balance(alice, bob["USD"](-1000)));
313
314 // cindy tries to claw from bob, and fails because trustline does
315 // not exist
316 env(claw(cindy, bob["USD"](200)), Ter(tecNO_LINE));
317 env.close();
318 }
319
320 // When a trustline is created between issuer and holder,
321 // we must make sure the holder is unable to claw back from
322 // the issuer by impersonating the issuer account.
323 //
324 // This must be tested bidirectionally for both accounts because the
325 // issuer could be either the low or high account in the trustline
326 // object
327 {
328 Env env(*this, features);
329
330 Account const alice{"alice"};
331 Account const bob{"bob"};
332
333 env.fund(XRP(1000), alice, bob);
334 env.close();
335
336 auto const usd = alice["USD"];
337 auto const cad = bob["CAD"];
338
339 // alice sets asfAllowTrustLineClawback
340 env(fset(alice, asfAllowTrustLineClawback));
341 env.close();
342 env.require(Flags(alice, asfAllowTrustLineClawback));
343
344 // bob sets asfAllowTrustLineClawback
345 env(fset(bob, asfAllowTrustLineClawback));
346 env.close();
347 env.require(Flags(bob, asfAllowTrustLineClawback));
348
349 // alice issues 10 USD to bob.
350 // bob then attempts to submit a clawback tx to claw USD from alice.
351 // this must FAIL, because bob is not the issuer for this
352 // trustline!!!
353 {
354 // bob creates a trustline with alice, and alice sends 10 USD to
355 // bob
356 env.trust(usd(1000), bob);
357 env(pay(alice, bob, usd(10)));
358 env.close();
359
360 env.require(Balance(bob, alice["USD"](10)));
361 env.require(Balance(alice, bob["USD"](-10)));
362
363 // bob cannot claw back USD from alice because he's not the
364 // issuer
365 env(claw(bob, alice["USD"](5)), Ter(tecNO_PERMISSION));
366 env.close();
367 }
368
369 // bob issues 10 CAD to alice.
370 // alice then attempts to submit a clawback tx to claw CAD from bob.
371 // this must FAIL, because alice is not the issuer for this
372 // trustline!!!
373 {
374 // alice creates a trustline with bob, and bob sends 10 CAD to
375 // alice
376 env.trust(cad(1000), alice);
377 env(pay(bob, alice, cad(10)));
378 env.close();
379
380 env.require(Balance(bob, alice["CAD"](-10)));
381 env.require(Balance(alice, bob["CAD"](10)));
382
383 // alice cannot claw back CAD from bob because she's not the
384 // issuer
385 env(claw(alice, bob["CAD"](5)), Ter(tecNO_PERMISSION));
386 env.close();
387 }
388 }
389 }
390
391 void
393 {
394 testcase("Enable clawback");
395 using namespace test::jtx;
396
397 // Test that alice is able to successfully clawback tokens from bob
398 Env env(*this, features);
399
400 Account const alice{"alice"};
401 Account const bob{"bob"};
402
403 env.fund(XRP(1000), alice, bob);
404 env.close();
405
406 auto const usd = alice["USD"];
407
408 // alice sets asfAllowTrustLineClawback
409 env(fset(alice, asfAllowTrustLineClawback));
410 env.close();
411 env.require(Flags(alice, asfAllowTrustLineClawback));
412
413 // alice issues 1000 USD to bob
414 env.trust(usd(1000), bob);
415 env(pay(alice, bob, usd(1000)));
416 env.close();
417
418 env.require(Balance(bob, alice["USD"](1000)));
419 env.require(Balance(alice, bob["USD"](-1000)));
420
421 // alice claws back 200 USD from bob
422 env(claw(alice, bob["USD"](200)));
423 env.close();
424
425 // bob should have 800 USD left
426 env.require(Balance(bob, alice["USD"](800)));
427 env.require(Balance(alice, bob["USD"](-800)));
428
429 // alice claws back 800 USD from bob again
430 env(claw(alice, bob["USD"](800)));
431 env.close();
432
433 // trustline has a balance of 0
434 env.require(Balance(bob, alice["USD"](0)));
435 env.require(Balance(alice, bob["USD"](0)));
436 }
437
438 void
440 {
441 // Test scenarios where multiple trustlines are involved
442 testcase("Multi line");
443 using namespace test::jtx;
444
445 // Both alice and bob issues their own "USD" to cindy.
446 // When alice and bob tries to claw back, they will only
447 // claw back from their respective trustline.
448 {
449 Env env(*this, features);
450
451 Account const alice{"alice"};
452 Account const bob{"bob"};
453 Account const cindy{"cindy"};
454
455 env.fund(XRP(1000), alice, bob, cindy);
456 env.close();
457
458 // alice sets asfAllowTrustLineClawback
459 env(fset(alice, asfAllowTrustLineClawback));
460 env.close();
461 env.require(Flags(alice, asfAllowTrustLineClawback));
462
463 // bob sets asfAllowTrustLineClawback
464 env(fset(bob, asfAllowTrustLineClawback));
465 env.close();
466 env.require(Flags(bob, asfAllowTrustLineClawback));
467
468 // alice sends 1000 USD to cindy
469 env.trust(alice["USD"](1000), cindy);
470 env(pay(alice, cindy, alice["USD"](1000)));
471 env.close();
472
473 // bob sends 1000 USD to cindy
474 env.trust(bob["USD"](1000), cindy);
475 env(pay(bob, cindy, bob["USD"](1000)));
476 env.close();
477
478 // alice claws back 200 USD from cindy
479 env(claw(alice, cindy["USD"](200)));
480 env.close();
481
482 // cindy has 800 USD left in alice's trustline after clawed by alice
483 env.require(Balance(cindy, alice["USD"](800)));
484 env.require(Balance(alice, cindy["USD"](-800)));
485
486 // cindy still has 1000 USD in bob's trustline
487 env.require(Balance(cindy, bob["USD"](1000)));
488 env.require(Balance(bob, cindy["USD"](-1000)));
489
490 // bob claws back 600 USD from cindy
491 env(claw(bob, cindy["USD"](600)));
492 env.close();
493
494 // cindy has 400 USD left in bob's trustline after clawed by bob
495 env.require(Balance(cindy, bob["USD"](400)));
496 env.require(Balance(bob, cindy["USD"](-400)));
497
498 // cindy still has 800 USD in alice's trustline
499 env.require(Balance(cindy, alice["USD"](800)));
500 env.require(Balance(alice, cindy["USD"](-800)));
501 }
502
503 // alice issues USD to both bob and cindy.
504 // when alice claws back from bob, only bob's USD balance is
505 // affected, and cindy's balance remains unchanged, and vice versa.
506 {
507 Env env(*this, features);
508
509 Account const alice{"alice"};
510 Account const bob{"bob"};
511 Account const cindy{"cindy"};
512
513 env.fund(XRP(1000), alice, bob, cindy);
514 env.close();
515
516 auto const usd = alice["USD"];
517
518 // alice sets asfAllowTrustLineClawback
519 env(fset(alice, asfAllowTrustLineClawback));
520 env.close();
521 env.require(Flags(alice, asfAllowTrustLineClawback));
522
523 // alice sends 600 USD to bob
524 env.trust(usd(1000), bob);
525 env(pay(alice, bob, usd(600)));
526 env.close();
527
528 env.require(Balance(alice, bob["USD"](-600)));
529 env.require(Balance(bob, alice["USD"](600)));
530
531 // alice sends 1000 USD to cindy
532 env.trust(usd(1000), cindy);
533 env(pay(alice, cindy, usd(1000)));
534 env.close();
535
536 env.require(Balance(alice, cindy["USD"](-1000)));
537 env.require(Balance(cindy, alice["USD"](1000)));
538
539 // alice claws back 500 USD from bob
540 env(claw(alice, bob["USD"](500)));
541 env.close();
542
543 // bob's balance is reduced
544 env.require(Balance(alice, bob["USD"](-100)));
545 env.require(Balance(bob, alice["USD"](100)));
546
547 // cindy's balance is unchanged
548 env.require(Balance(alice, cindy["USD"](-1000)));
549 env.require(Balance(cindy, alice["USD"](1000)));
550
551 // alice claws back 300 USD from cindy
552 env(claw(alice, cindy["USD"](300)));
553 env.close();
554
555 // bob's balance is unchanged
556 env.require(Balance(alice, bob["USD"](-100)));
557 env.require(Balance(bob, alice["USD"](100)));
558
559 // cindy's balance is reduced
560 env.require(Balance(alice, cindy["USD"](-700)));
561 env.require(Balance(cindy, alice["USD"](700)));
562 }
563 }
564
565 void
567 {
568 testcase("Bidirectional line");
569 using namespace test::jtx;
570
571 // Test when both alice and bob issues USD to each other.
572 // This scenario creates only one trustline.
573 // In this case, both alice and bob can be seen as the "issuer"
574 // and they can send however many USDs to each other.
575 // We test that only the person who has a negative balance from their
576 // perspective is allowed to clawback
577 Env env(*this, features);
578
579 Account const alice{"alice"};
580 Account const bob{"bob"};
581
582 env.fund(XRP(1000), alice, bob);
583 env.close();
584
585 // alice sets asfAllowTrustLineClawback
586 env(fset(alice, asfAllowTrustLineClawback));
587 env.close();
588 env.require(Flags(alice, asfAllowTrustLineClawback));
589
590 // bob sets asfAllowTrustLineClawback
591 env(fset(bob, asfAllowTrustLineClawback));
592 env.close();
593 env.require(Flags(bob, asfAllowTrustLineClawback));
594
595 // alice issues 1000 USD to bob
596 env.trust(alice["USD"](1000), bob);
597 env(pay(alice, bob, alice["USD"](1000)));
598 env.close();
599
600 BEAST_EXPECT(ownerCount(env, alice) == 0);
601 BEAST_EXPECT(ownerCount(env, bob) == 1);
602
603 // bob is the holder, and alice is the issuer
604 env.require(Balance(bob, alice["USD"](1000)));
605 env.require(Balance(alice, bob["USD"](-1000)));
606
607 // bob issues 1500 USD to alice
608 env.trust(bob["USD"](1500), alice);
609 env(pay(bob, alice, bob["USD"](1500)));
610 env.close();
611
612 BEAST_EXPECT(ownerCount(env, alice) == 1);
613 BEAST_EXPECT(ownerCount(env, bob) == 1);
614
615 // bob has negative 500 USD because bob issued 500 USD more than alice
616 // bob can now been seen as the issuer, while alice is the holder
617 env.require(Balance(bob, alice["USD"](-500)));
618 env.require(Balance(alice, bob["USD"](500)));
619
620 // At this point, both alice and bob are the issuers of USD
621 // and can send USD to each other through one trustline
622
623 // alice fails to clawback. Even though she is also an issuer,
624 // the trustline balance is positive from her perspective
625 env(claw(alice, bob["USD"](200)), Ter(tecNO_PERMISSION));
626 env.close();
627
628 // bob is able to successfully clawback from alice because
629 // the trustline balance is negative from his perspective
630 env(claw(bob, alice["USD"](200)));
631 env.close();
632
633 env.require(Balance(bob, alice["USD"](-300)));
634 env.require(Balance(alice, bob["USD"](300)));
635
636 // alice pays bob 1000 USD
637 env(pay(alice, bob, alice["USD"](1000)));
638 env.close();
639
640 // bob's balance becomes positive from his perspective because
641 // alice issued more USD than the balance
642 env.require(Balance(bob, alice["USD"](700)));
643 env.require(Balance(alice, bob["USD"](-700)));
644
645 // bob is now the holder and fails to clawback
646 env(claw(bob, alice["USD"](200)), Ter(tecNO_PERMISSION));
647 env.close();
648
649 // alice successfully claws back
650 env(claw(alice, bob["USD"](200)));
651 env.close();
652
653 env.require(Balance(bob, alice["USD"](500)));
654 env.require(Balance(alice, bob["USD"](-500)));
655 }
656
657 void
659 {
660 testcase("Delete default trustline");
661 using namespace test::jtx;
662
663 // If clawback results the trustline to be default,
664 // trustline should be automatically deleted
665 Env env(*this, features);
666 Account const alice{"alice"};
667 Account const bob{"bob"};
668
669 env.fund(XRP(1000), alice, bob);
670 env.close();
671
672 auto const usd = alice["USD"];
673
674 // alice sets asfAllowTrustLineClawback
675 env(fset(alice, asfAllowTrustLineClawback));
676 env.close();
677 env.require(Flags(alice, asfAllowTrustLineClawback));
678
679 // alice issues 1000 USD to bob
680 env.trust(usd(1000), bob);
681 env(pay(alice, bob, usd(1000)));
682 env.close();
683
684 BEAST_EXPECT(ownerCount(env, alice) == 0);
685 BEAST_EXPECT(ownerCount(env, bob) == 1);
686
687 env.require(Balance(bob, alice["USD"](1000)));
688 env.require(Balance(alice, bob["USD"](-1000)));
689
690 // set limit to default,
691 env(trust(bob, usd(0), 0));
692 env.close();
693
694 BEAST_EXPECT(ownerCount(env, alice) == 0);
695 BEAST_EXPECT(ownerCount(env, bob) == 1);
696
697 // alice claws back full amount from bob, and should also delete
698 // trustline
699 env(claw(alice, bob["USD"](1000)));
700 env.close();
701
702 // bob no longer owns the trustline because it was deleted
703 BEAST_EXPECT(ownerCount(env, alice) == 0);
704 BEAST_EXPECT(ownerCount(env, bob) == 0);
705 }
706
707 void
709 {
710 testcase("Frozen trustline");
711 using namespace test::jtx;
712
713 // Claws back from frozen trustline
714 // and the trustline should remain frozen
715 Env env(*this, features);
716 Account const alice{"alice"};
717 Account const bob{"bob"};
718
719 env.fund(XRP(1000), alice, bob);
720 env.close();
721
722 auto const usd = alice["USD"];
723
724 // alice sets asfAllowTrustLineClawback
725 env(fset(alice, asfAllowTrustLineClawback));
726 env.close();
727 env.require(Flags(alice, asfAllowTrustLineClawback));
728
729 // alice issues 1000 USD to bob
730 env.trust(usd(1000), bob);
731 env(pay(alice, bob, usd(1000)));
732 env.close();
733
734 env.require(Balance(bob, alice["USD"](1000)));
735 env.require(Balance(alice, bob["USD"](-1000)));
736
737 // freeze trustline
738 env(trust(alice, bob["USD"](0), tfSetFreeze));
739 env.close();
740
741 // alice claws back 200 USD from bob
742 env(claw(alice, bob["USD"](200)));
743 env.close();
744
745 // bob should have 800 USD left
746 env.require(Balance(bob, alice["USD"](800)));
747 env.require(Balance(alice, bob["USD"](-800)));
748
749 // trustline remains frozen
750 BEAST_EXPECT(getLineFreezeFlag(env, alice, bob, usd.currency));
751 }
752
753 void
755 {
756 testcase("Amount exceeds available");
757 using namespace test::jtx;
758
759 // When alice tries to claw back an amount that is greater
760 // than what bob holds, only the max available balance is clawed
761 Env env(*this, features);
762 Account const alice{"alice"};
763 Account const bob{"bob"};
764
765 env.fund(XRP(1000), alice, bob);
766 env.close();
767
768 auto const usd = alice["USD"];
769
770 // alice sets asfAllowTrustLineClawback
771 env(fset(alice, asfAllowTrustLineClawback));
772 env.close();
773 env.require(Flags(alice, asfAllowTrustLineClawback));
774
775 // alice issues 1000 USD to bob
776 env.trust(usd(1000), bob);
777 env(pay(alice, bob, usd(1000)));
778 env.close();
779
780 env.require(Balance(bob, alice["USD"](1000)));
781 env.require(Balance(alice, bob["USD"](-1000)));
782
783 // alice tries to claw back 2000 USD
784 env(claw(alice, bob["USD"](2000)));
785 env.close();
786
787 // check alice and bob's balance.
788 // alice was only able to claw back 1000 USD at maximum
789 env.require(Balance(bob, alice["USD"](0)));
790 env.require(Balance(alice, bob["USD"](0)));
791
792 // bob still owns the trustline because trustline is not in default
793 // state
794 BEAST_EXPECT(ownerCount(env, alice) == 0);
795 BEAST_EXPECT(ownerCount(env, bob) == 1);
796
797 // set limit to default,
798 env(trust(bob, usd(0), 0));
799 env.close();
800
801 // verify that bob's trustline was deleted
802 BEAST_EXPECT(ownerCount(env, alice) == 0);
803 BEAST_EXPECT(ownerCount(env, bob) == 0);
804 }
805
806 void
808 {
809 testcase("Tickets");
810 using namespace test::jtx;
811
812 // Tests clawback with tickets
813 Env env(*this, features);
814 Account const alice{"alice"};
815 Account const bob{"bob"};
816
817 env.fund(XRP(1000), alice, bob);
818 env.close();
819
820 auto const usd = alice["USD"];
821
822 // alice sets asfAllowTrustLineClawback
823 env(fset(alice, asfAllowTrustLineClawback));
824 env.close();
825 env.require(Flags(alice, asfAllowTrustLineClawback));
826
827 // alice issues 100 USD to bob
828 env.trust(usd(1000), bob);
829 env(pay(alice, bob, usd(100)));
830 env.close();
831
832 env.require(Balance(bob, alice["USD"](100)));
833 env.require(Balance(alice, bob["USD"](-100)));
834
835 // alice creates 10 tickets
836 std::uint32_t ticketCnt = 10;
837 std::uint32_t aliceTicketSeq{env.seq(alice) + 1};
838 env(ticket::create(alice, ticketCnt));
839 env.close();
840 std::uint32_t const aliceSeq{env.seq(alice)};
841 BEAST_EXPECT(ticketCount(env, alice) == ticketCnt);
842 BEAST_EXPECT(ownerCount(env, alice) == ticketCnt);
843
844 while (ticketCnt > 0)
845 {
846 // alice claws back 5 USD using a ticket
847 env(claw(alice, bob["USD"](5)), ticket::Use(aliceTicketSeq++));
848 env.close();
849
850 ticketCnt--;
851 BEAST_EXPECT(ticketCount(env, alice) == ticketCnt);
852 BEAST_EXPECT(ownerCount(env, alice) == ticketCnt);
853 }
854
855 // alice clawed back 50 USD total, trustline has 50 USD remaining
856 env.require(Balance(bob, alice["USD"](50)));
857 env.require(Balance(alice, bob["USD"](-50)));
858
859 // Verify that the account sequence numbers did not advance.
860 BEAST_EXPECT(env.seq(alice) == aliceSeq);
861 }
862
863 void
865 {
867 testValidation(features);
868 testPermission(features);
869 testEnabled(features);
870 testMultiLine(features);
871 testBidirectionalLine(features);
872 testDeleteDefaultLine(features);
873 testFrozenLine(features);
875 testTickets(features);
876 }
877
878public:
879 void
880 run() override
881 {
882 using namespace test::jtx;
883 FeatureBitset const all{testableAmendments()};
884
885 testWithFeats(all - featureMPTokensV1);
886 testWithFeats(all);
887 }
888};
889
891} // namespace xrpl
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
static std::string toString(T const &t)
void testDeleteDefaultLine(FeatureBitset features)
void testTickets(FeatureBitset features)
static std::uint32_t ticketCount(test::jtx::Env const &env, test::jtx::Account const &acct)
void testBidirectionalLine(FeatureBitset features)
void run() override
Runs the suite.
void testAmountExceedsAvailable(FeatureBitset features)
void testEnabled(FeatureBitset features)
void testFrozenLine(FeatureBitset features)
void testWithFeats(FeatureBitset features)
void testPermission(FeatureBitset features)
void testMultiLine(FeatureBitset features)
void testValidation(FeatureBitset features)
static bool getLineFreezeFlag(test::jtx::Env const &env, test::jtx::Account const &src, test::jtx::Account const &dst, Currency const &cur)
void testAllowTrustLineClawbackFlag(FeatureBitset features)
Immutable cryptographic account descriptor.
Definition jtx/Account.h:17
AccountID id() const
Returns the Account ID.
Definition jtx/Account.h:85
A transaction testing environment.
Definition Env.h:143
SLE::const_pointer le(Account const &account) const
Return an account root.
Definition Env.cpp:284
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:241
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_ACCOUNT
Definition TER.h:209
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:36
@ temINVALID_FLAG
Definition TER.h:97
@ temBAD_AMOUNT
Definition TER.h:75
@ tecINSUFFICIENT_FUNDS
Definition TER.h:323
@ tecNO_LINE
Definition TER.h:299
@ tecOWNERS
Definition TER.h:296
@ tecNO_PERMISSION
Definition TER.h:303
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:49