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 ripple {
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, ripple);
941} // namespace ripple
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
void testDeleteDefaultLine(FeatureBitset features)
void testPermission(FeatureBitset features)
void testBidirectionalLine(FeatureBitset features)
void testAmountExceedsAvailable(FeatureBitset features)
void run() override
Runs the suite.
static std::uint32_t ticketCount(test::jtx::Env const &env, test::jtx::Account const &acct)
static bool getLineFreezeFlag(test::jtx::Env const &env, test::jtx::Account const &src, test::jtx::Account const &dst, Currency const &cur)
void testTickets(FeatureBitset features)
void testWithFeats(FeatureBitset features)
static std::string to_string(T const &t)
void testMultiLine(FeatureBitset features)
void testFrozenLine(FeatureBitset features)
void testEnabled(FeatureBitset features)
void testAllowTrustLineClawbackFlag(FeatureBitset features)
void testValidation(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:259
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:225
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
constexpr std::uint32_t asfNoFreeze
Definition TxFlags.h:63
@ tecOWNERS
Definition TER.h:280
@ tecINSUFFICIENT_FUNDS
Definition TER.h:307
@ tecNO_PERMISSION
Definition TER.h:287
@ tecNO_LINE
Definition TER.h:283
constexpr std::uint32_t asfAllowTrustLineClawback
Definition TxFlags.h:75
@ terNO_ACCOUNT
Definition TER.h:198
constexpr std::uint32_t tfSetFreeze
Definition TxFlags.h:99
@ temBAD_AMOUNT
Definition TER.h:70
@ temINVALID_FLAG
Definition TER.h:92
@ temDISABLED
Definition TER.h:95