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