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)),
254 txflags(0x00008000),
255 ter(temINVALID_FLAG));
256 env.close();
257
258 // fails due to negative amount
259 env(claw(alice, bob["USD"](-5)), ter(temBAD_AMOUNT));
260 env.close();
261
262 // fails due to zero amount
263 env(claw(alice, bob["USD"](0)), ter(temBAD_AMOUNT));
264 env.close();
265
266 // fails because amount is in XRP
267 env(claw(alice, XRP(10)), ter(temBAD_AMOUNT));
268 env.close();
269
270 // fails when `issuer` field in `amount` is not token holder
271 // NOTE: we are using the `issuer` field for the token holder
272 env(claw(alice, alice["USD"](5)), ter(temBAD_AMOUNT));
273 env.close();
274
275 // bob pays alice back, trustline has a balance of 0
276 env(pay(bob, alice, USD(10)));
277 env.close();
278
279 // bob still owns the trustline that has 0 balance
280 BEAST_EXPECT(ownerCount(env, alice) == 0);
281 BEAST_EXPECT(ownerCount(env, bob) == 1);
282 env.require(balance(bob, alice["USD"](0)));
283 env.require(balance(alice, bob["USD"](0)));
284
285 // clawback fails because because balance is 0
286 env(claw(alice, bob["USD"](5)), ter(tecINSUFFICIENT_FUNDS));
287 env.close();
288
289 // set the limit to default, which should delete the trustline
290 env(trust(bob, USD(0), 0));
291 env.close();
292
293 // bob no longer owns the trustline
294 BEAST_EXPECT(ownerCount(env, alice) == 0);
295 BEAST_EXPECT(ownerCount(env, bob) == 0);
296
297 // clawback fails because trustline does not exist
298 env(claw(alice, bob["USD"](5)), ter(tecNO_LINE));
299 env.close();
300 }
301 }
302
303 void
305 {
306 // Checks the tx submitter has the permission to clawback.
307 // Exercises preclaim code
308 testcase("Permission");
309 using namespace test::jtx;
310
311 // Clawing back from an non-existent account returns error
312 {
313 Env env(*this, features);
314
315 Account alice{"alice"};
316 Account bob{"bob"};
317
318 // bob's account is not funded and does not exist
319 env.fund(XRP(1000), alice);
320 env.close();
321
322 // alice sets asfAllowTrustLineClawback
323 env(fset(alice, asfAllowTrustLineClawback));
324 env.close();
325 env.require(flags(alice, asfAllowTrustLineClawback));
326
327 // bob, the token holder, does not exist
328 env(claw(alice, bob["USD"](5)), ter(terNO_ACCOUNT));
329 env.close();
330 }
331
332 // Test that trustline cannot be clawed by someone who is
333 // not the issuer of the currency
334 {
335 Env env(*this, features);
336
337 Account alice{"alice"};
338 Account bob{"bob"};
339 Account cindy{"cindy"};
340
341 env.fund(XRP(1000), alice, bob, cindy);
342 env.close();
343
344 auto const USD = alice["USD"];
345
346 // alice sets asfAllowTrustLineClawback
347 env(fset(alice, asfAllowTrustLineClawback));
348 env.close();
349 env.require(flags(alice, asfAllowTrustLineClawback));
350
351 // cindy sets asfAllowTrustLineClawback
352 env(fset(cindy, asfAllowTrustLineClawback));
353 env.close();
354 env.require(flags(cindy, asfAllowTrustLineClawback));
355
356 // alice issues 1000 USD to bob
357 env.trust(USD(1000), bob);
358 env(pay(alice, bob, USD(1000)));
359 env.close();
360
361 env.require(balance(bob, alice["USD"](1000)));
362 env.require(balance(alice, bob["USD"](-1000)));
363
364 // cindy tries to claw from bob, and fails because trustline does
365 // not exist
366 env(claw(cindy, bob["USD"](200)), ter(tecNO_LINE));
367 env.close();
368 }
369
370 // When a trustline is created between issuer and holder,
371 // we must make sure the holder is unable to claw back from
372 // the issuer by impersonating the issuer account.
373 //
374 // This must be tested bidirectionally for both accounts because the
375 // issuer could be either the low or high account in the trustline
376 // object
377 {
378 Env env(*this, features);
379
380 Account alice{"alice"};
381 Account bob{"bob"};
382
383 env.fund(XRP(1000), alice, bob);
384 env.close();
385
386 auto const USD = alice["USD"];
387 auto const CAD = bob["CAD"];
388
389 // alice sets asfAllowTrustLineClawback
390 env(fset(alice, asfAllowTrustLineClawback));
391 env.close();
392 env.require(flags(alice, asfAllowTrustLineClawback));
393
394 // bob sets asfAllowTrustLineClawback
395 env(fset(bob, asfAllowTrustLineClawback));
396 env.close();
397 env.require(flags(bob, asfAllowTrustLineClawback));
398
399 // alice issues 10 USD to bob.
400 // bob then attempts to submit a clawback tx to claw USD from alice.
401 // this must FAIL, because bob is not the issuer for this
402 // trustline!!!
403 {
404 // bob creates a trustline with alice, and alice sends 10 USD to
405 // bob
406 env.trust(USD(1000), bob);
407 env(pay(alice, bob, USD(10)));
408 env.close();
409
410 env.require(balance(bob, alice["USD"](10)));
411 env.require(balance(alice, bob["USD"](-10)));
412
413 // bob cannot claw back USD from alice because he's not the
414 // issuer
415 env(claw(bob, alice["USD"](5)), ter(tecNO_PERMISSION));
416 env.close();
417 }
418
419 // bob issues 10 CAD to alice.
420 // alice then attempts to submit a clawback tx to claw CAD from bob.
421 // this must FAIL, because alice is not the issuer for this
422 // trustline!!!
423 {
424 // alice creates a trustline with bob, and bob sends 10 CAD to
425 // alice
426 env.trust(CAD(1000), alice);
427 env(pay(bob, alice, CAD(10)));
428 env.close();
429
430 env.require(balance(bob, alice["CAD"](-10)));
431 env.require(balance(alice, bob["CAD"](10)));
432
433 // alice cannot claw back CAD from bob because she's not the
434 // issuer
435 env(claw(alice, bob["CAD"](5)), ter(tecNO_PERMISSION));
436 env.close();
437 }
438 }
439 }
440
441 void
443 {
444 testcase("Enable clawback");
445 using namespace test::jtx;
446
447 // Test that alice is able to successfully clawback tokens from bob
448 Env env(*this, features);
449
450 Account alice{"alice"};
451 Account bob{"bob"};
452
453 env.fund(XRP(1000), alice, bob);
454 env.close();
455
456 auto const USD = alice["USD"];
457
458 // alice sets asfAllowTrustLineClawback
459 env(fset(alice, asfAllowTrustLineClawback));
460 env.close();
461 env.require(flags(alice, asfAllowTrustLineClawback));
462
463 // alice issues 1000 USD to bob
464 env.trust(USD(1000), bob);
465 env(pay(alice, bob, USD(1000)));
466 env.close();
467
468 env.require(balance(bob, alice["USD"](1000)));
469 env.require(balance(alice, bob["USD"](-1000)));
470
471 // alice claws back 200 USD from bob
472 env(claw(alice, bob["USD"](200)));
473 env.close();
474
475 // bob should have 800 USD left
476 env.require(balance(bob, alice["USD"](800)));
477 env.require(balance(alice, bob["USD"](-800)));
478
479 // alice claws back 800 USD from bob again
480 env(claw(alice, bob["USD"](800)));
481 env.close();
482
483 // trustline has a balance of 0
484 env.require(balance(bob, alice["USD"](0)));
485 env.require(balance(alice, bob["USD"](0)));
486 }
487
488 void
490 {
491 // Test scenarios where multiple trustlines are involved
492 testcase("Multi line");
493 using namespace test::jtx;
494
495 // Both alice and bob issues their own "USD" to cindy.
496 // When alice and bob tries to claw back, they will only
497 // claw back from their respective trustline.
498 {
499 Env env(*this, features);
500
501 Account alice{"alice"};
502 Account bob{"bob"};
503 Account cindy{"cindy"};
504
505 env.fund(XRP(1000), alice, bob, cindy);
506 env.close();
507
508 // alice sets asfAllowTrustLineClawback
509 env(fset(alice, asfAllowTrustLineClawback));
510 env.close();
511 env.require(flags(alice, asfAllowTrustLineClawback));
512
513 // bob sets asfAllowTrustLineClawback
514 env(fset(bob, asfAllowTrustLineClawback));
515 env.close();
516 env.require(flags(bob, asfAllowTrustLineClawback));
517
518 // alice sends 1000 USD to cindy
519 env.trust(alice["USD"](1000), cindy);
520 env(pay(alice, cindy, alice["USD"](1000)));
521 env.close();
522
523 // bob sends 1000 USD to cindy
524 env.trust(bob["USD"](1000), cindy);
525 env(pay(bob, cindy, bob["USD"](1000)));
526 env.close();
527
528 // alice claws back 200 USD from cindy
529 env(claw(alice, cindy["USD"](200)));
530 env.close();
531
532 // cindy has 800 USD left in alice's trustline after clawed by alice
533 env.require(balance(cindy, alice["USD"](800)));
534 env.require(balance(alice, cindy["USD"](-800)));
535
536 // cindy still has 1000 USD in bob's trustline
537 env.require(balance(cindy, bob["USD"](1000)));
538 env.require(balance(bob, cindy["USD"](-1000)));
539
540 // bob claws back 600 USD from cindy
541 env(claw(bob, cindy["USD"](600)));
542 env.close();
543
544 // cindy has 400 USD left in bob's trustline after clawed by bob
545 env.require(balance(cindy, bob["USD"](400)));
546 env.require(balance(bob, cindy["USD"](-400)));
547
548 // cindy still has 800 USD in alice's trustline
549 env.require(balance(cindy, alice["USD"](800)));
550 env.require(balance(alice, cindy["USD"](-800)));
551 }
552
553 // alice issues USD to both bob and cindy.
554 // when alice claws back from bob, only bob's USD balance is
555 // affected, and cindy's balance remains unchanged, and vice versa.
556 {
557 Env env(*this, features);
558
559 Account alice{"alice"};
560 Account bob{"bob"};
561 Account cindy{"cindy"};
562
563 env.fund(XRP(1000), alice, bob, cindy);
564 env.close();
565
566 auto const USD = alice["USD"];
567
568 // alice sets asfAllowTrustLineClawback
569 env(fset(alice, asfAllowTrustLineClawback));
570 env.close();
571 env.require(flags(alice, asfAllowTrustLineClawback));
572
573 // alice sends 600 USD to bob
574 env.trust(USD(1000), bob);
575 env(pay(alice, bob, USD(600)));
576 env.close();
577
578 env.require(balance(alice, bob["USD"](-600)));
579 env.require(balance(bob, alice["USD"](600)));
580
581 // alice sends 1000 USD to cindy
582 env.trust(USD(1000), cindy);
583 env(pay(alice, cindy, USD(1000)));
584 env.close();
585
586 env.require(balance(alice, cindy["USD"](-1000)));
587 env.require(balance(cindy, alice["USD"](1000)));
588
589 // alice claws back 500 USD from bob
590 env(claw(alice, bob["USD"](500)));
591 env.close();
592
593 // bob's balance is reduced
594 env.require(balance(alice, bob["USD"](-100)));
595 env.require(balance(bob, alice["USD"](100)));
596
597 // cindy's balance is unchanged
598 env.require(balance(alice, cindy["USD"](-1000)));
599 env.require(balance(cindy, alice["USD"](1000)));
600
601 // alice claws back 300 USD from cindy
602 env(claw(alice, cindy["USD"](300)));
603 env.close();
604
605 // bob's balance is unchanged
606 env.require(balance(alice, bob["USD"](-100)));
607 env.require(balance(bob, alice["USD"](100)));
608
609 // cindy's balance is reduced
610 env.require(balance(alice, cindy["USD"](-700)));
611 env.require(balance(cindy, alice["USD"](700)));
612 }
613 }
614
615 void
617 {
618 testcase("Bidirectional line");
619 using namespace test::jtx;
620
621 // Test when both alice and bob issues USD to each other.
622 // This scenario creates only one trustline.
623 // In this case, both alice and bob can be seen as the "issuer"
624 // and they can send however many USDs to each other.
625 // We test that only the person who has a negative balance from their
626 // perspective is allowed to clawback
627 Env env(*this, features);
628
629 Account alice{"alice"};
630 Account bob{"bob"};
631
632 env.fund(XRP(1000), alice, bob);
633 env.close();
634
635 // alice sets asfAllowTrustLineClawback
636 env(fset(alice, asfAllowTrustLineClawback));
637 env.close();
638 env.require(flags(alice, asfAllowTrustLineClawback));
639
640 // bob sets asfAllowTrustLineClawback
641 env(fset(bob, asfAllowTrustLineClawback));
642 env.close();
643 env.require(flags(bob, asfAllowTrustLineClawback));
644
645 // alice issues 1000 USD to bob
646 env.trust(alice["USD"](1000), bob);
647 env(pay(alice, bob, alice["USD"](1000)));
648 env.close();
649
650 BEAST_EXPECT(ownerCount(env, alice) == 0);
651 BEAST_EXPECT(ownerCount(env, bob) == 1);
652
653 // bob is the holder, and alice is the issuer
654 env.require(balance(bob, alice["USD"](1000)));
655 env.require(balance(alice, bob["USD"](-1000)));
656
657 // bob issues 1500 USD to alice
658 env.trust(bob["USD"](1500), alice);
659 env(pay(bob, alice, bob["USD"](1500)));
660 env.close();
661
662 BEAST_EXPECT(ownerCount(env, alice) == 1);
663 BEAST_EXPECT(ownerCount(env, bob) == 1);
664
665 // bob has negative 500 USD because bob issued 500 USD more than alice
666 // bob can now been seen as the issuer, while alice is the holder
667 env.require(balance(bob, alice["USD"](-500)));
668 env.require(balance(alice, bob["USD"](500)));
669
670 // At this point, both alice and bob are the issuers of USD
671 // and can send USD to each other through one trustline
672
673 // alice fails to clawback. Even though she is also an issuer,
674 // the trustline balance is positive from her perspective
675 env(claw(alice, bob["USD"](200)), ter(tecNO_PERMISSION));
676 env.close();
677
678 // bob is able to successfully clawback from alice because
679 // the trustline balance is negative from his perspective
680 env(claw(bob, alice["USD"](200)));
681 env.close();
682
683 env.require(balance(bob, alice["USD"](-300)));
684 env.require(balance(alice, bob["USD"](300)));
685
686 // alice pays bob 1000 USD
687 env(pay(alice, bob, alice["USD"](1000)));
688 env.close();
689
690 // bob's balance becomes positive from his perspective because
691 // alice issued more USD than the balance
692 env.require(balance(bob, alice["USD"](700)));
693 env.require(balance(alice, bob["USD"](-700)));
694
695 // bob is now the holder and fails to clawback
696 env(claw(bob, alice["USD"](200)), ter(tecNO_PERMISSION));
697 env.close();
698
699 // alice successfully claws back
700 env(claw(alice, bob["USD"](200)));
701 env.close();
702
703 env.require(balance(bob, alice["USD"](500)));
704 env.require(balance(alice, bob["USD"](-500)));
705 }
706
707 void
709 {
710 testcase("Delete default trustline");
711 using namespace test::jtx;
712
713 // If clawback results the trustline to be default,
714 // trustline should be automatically deleted
715 Env env(*this, features);
716 Account alice{"alice"};
717 Account 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 BEAST_EXPECT(ownerCount(env, alice) == 0);
735 BEAST_EXPECT(ownerCount(env, bob) == 1);
736
737 env.require(balance(bob, alice["USD"](1000)));
738 env.require(balance(alice, bob["USD"](-1000)));
739
740 // set limit to default,
741 env(trust(bob, USD(0), 0));
742 env.close();
743
744 BEAST_EXPECT(ownerCount(env, alice) == 0);
745 BEAST_EXPECT(ownerCount(env, bob) == 1);
746
747 // alice claws back full amount from bob, and should also delete
748 // trustline
749 env(claw(alice, bob["USD"](1000)));
750 env.close();
751
752 // bob no longer owns the trustline because it was deleted
753 BEAST_EXPECT(ownerCount(env, alice) == 0);
754 BEAST_EXPECT(ownerCount(env, bob) == 0);
755 }
756
757 void
759 {
760 testcase("Frozen trustline");
761 using namespace test::jtx;
762
763 // Claws back from frozen trustline
764 // and the trustline should remain frozen
765 Env env(*this, features);
766 Account alice{"alice"};
767 Account bob{"bob"};
768
769 env.fund(XRP(1000), alice, bob);
770 env.close();
771
772 auto const USD = alice["USD"];
773
774 // alice sets asfAllowTrustLineClawback
775 env(fset(alice, asfAllowTrustLineClawback));
776 env.close();
777 env.require(flags(alice, asfAllowTrustLineClawback));
778
779 // alice issues 1000 USD to bob
780 env.trust(USD(1000), bob);
781 env(pay(alice, bob, USD(1000)));
782 env.close();
783
784 env.require(balance(bob, alice["USD"](1000)));
785 env.require(balance(alice, bob["USD"](-1000)));
786
787 // freeze trustline
788 env(trust(alice, bob["USD"](0), tfSetFreeze));
789 env.close();
790
791 // alice claws back 200 USD from bob
792 env(claw(alice, bob["USD"](200)));
793 env.close();
794
795 // bob should have 800 USD left
796 env.require(balance(bob, alice["USD"](800)));
797 env.require(balance(alice, bob["USD"](-800)));
798
799 // trustline remains frozen
800 BEAST_EXPECT(getLineFreezeFlag(env, alice, bob, USD.currency));
801 }
802
803 void
805 {
806 testcase("Amount exceeds available");
807 using namespace test::jtx;
808
809 // When alice tries to claw back an amount that is greater
810 // than what bob holds, only the max available balance is clawed
811 Env env(*this, features);
812 Account alice{"alice"};
813 Account bob{"bob"};
814
815 env.fund(XRP(1000), alice, bob);
816 env.close();
817
818 auto const USD = alice["USD"];
819
820 // alice sets asfAllowTrustLineClawback
821 env(fset(alice, asfAllowTrustLineClawback));
822 env.close();
823 env.require(flags(alice, asfAllowTrustLineClawback));
824
825 // alice issues 1000 USD to bob
826 env.trust(USD(1000), bob);
827 env(pay(alice, bob, USD(1000)));
828 env.close();
829
830 env.require(balance(bob, alice["USD"](1000)));
831 env.require(balance(alice, bob["USD"](-1000)));
832
833 // alice tries to claw back 2000 USD
834 env(claw(alice, bob["USD"](2000)));
835 env.close();
836
837 // check alice and bob's balance.
838 // alice was only able to claw back 1000 USD at maximum
839 env.require(balance(bob, alice["USD"](0)));
840 env.require(balance(alice, bob["USD"](0)));
841
842 // bob still owns the trustline because trustline is not in default
843 // state
844 BEAST_EXPECT(ownerCount(env, alice) == 0);
845 BEAST_EXPECT(ownerCount(env, bob) == 1);
846
847 // set limit to default,
848 env(trust(bob, USD(0), 0));
849 env.close();
850
851 // verify that bob's trustline was deleted
852 BEAST_EXPECT(ownerCount(env, alice) == 0);
853 BEAST_EXPECT(ownerCount(env, bob) == 0);
854 }
855
856 void
858 {
859 testcase("Tickets");
860 using namespace test::jtx;
861
862 // Tests clawback with tickets
863 Env env(*this, features);
864 Account alice{"alice"};
865 Account bob{"bob"};
866
867 env.fund(XRP(1000), alice, bob);
868 env.close();
869
870 auto const USD = alice["USD"];
871
872 // alice sets asfAllowTrustLineClawback
873 env(fset(alice, asfAllowTrustLineClawback));
874 env.close();
875 env.require(flags(alice, asfAllowTrustLineClawback));
876
877 // alice issues 100 USD to bob
878 env.trust(USD(1000), bob);
879 env(pay(alice, bob, USD(100)));
880 env.close();
881
882 env.require(balance(bob, alice["USD"](100)));
883 env.require(balance(alice, bob["USD"](-100)));
884
885 // alice creates 10 tickets
886 std::uint32_t ticketCnt = 10;
887 std::uint32_t aliceTicketSeq{env.seq(alice) + 1};
888 env(ticket::create(alice, ticketCnt));
889 env.close();
890 std::uint32_t const aliceSeq{env.seq(alice)};
891 BEAST_EXPECT(ticketCount(env, alice) == ticketCnt);
892 BEAST_EXPECT(ownerCount(env, alice) == ticketCnt);
893
894 while (ticketCnt > 0)
895 {
896 // alice claws back 5 USD using a ticket
897 env(claw(alice, bob["USD"](5)), ticket::use(aliceTicketSeq++));
898 env.close();
899
900 ticketCnt--;
901 BEAST_EXPECT(ticketCount(env, alice) == ticketCnt);
902 BEAST_EXPECT(ownerCount(env, alice) == ticketCnt);
903 }
904
905 // alice clawed back 50 USD total, trustline has 50 USD remaining
906 env.require(balance(bob, alice["USD"](50)));
907 env.require(balance(alice, bob["USD"](-50)));
908
909 // Verify that the account sequence numbers did not advance.
910 BEAST_EXPECT(env.seq(alice) == aliceSeq);
911 }
912
913 void
915 {
917 testValidation(features);
918 testPermission(features);
919 testEnabled(features);
920 testMultiLine(features);
921 testBidirectionalLine(features);
922 testDeleteDefaultLine(features);
923 testFrozenLine(features);
925 testTickets(features);
926 }
927
928public:
929 void
930 run() override
931 {
932 using namespace test::jtx;
933 FeatureBitset const all{testable_amendments()};
934
935 testWithFeats(all - featureMPTokensV1);
937 }
938};
939
940BEAST_DEFINE_TESTSUITE(Clawback, app, xrpl);
941} // namespace xrpl
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
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:20
AccountID id() const
Returns the Account ID.
Definition Account.h:92
A transaction testing environment.
Definition Env.h:102
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:260
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:226
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
constexpr std::uint32_t asfAllowTrustLineClawback
Definition TxFlags.h:75
@ terNO_ACCOUNT
Definition TER.h:198
constexpr std::uint32_t asfNoFreeze
Definition TxFlags.h:63
@ temINVALID_FLAG
Definition TER.h:92
@ temDISABLED
Definition TER.h:95
@ temBAD_AMOUNT
Definition TER.h:70
@ tecINSUFFICIENT_FUNDS
Definition TER.h:307
@ tecNO_LINE
Definition TER.h:283
@ tecOWNERS
Definition TER.h:280
@ tecNO_PERMISSION
Definition TER.h:287
@ lsfLowFreeze
@ lsfHighFreeze
constexpr std::uint32_t tfSetFreeze
Definition TxFlags.h:99