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