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