xrpld
Loading...
Searching...
No Matches
VaultFreeze_test.cpp
1#include <test/app/vault/VaultTestBase.h>
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/flags.h>
6#include <test/jtx/mpt.h>
7#include <test/jtx/pay.h>
8#include <test/jtx/ter.h>
9#include <test/jtx/trust.h>
10#include <test/jtx/vault.h>
11
12#include <xrpl/basics/base_uint.h>
13#include <xrpl/beast/unit_test/suite.h>
14#include <xrpl/json/json_forwards.h>
15#include <xrpl/json/json_value.h>
16#include <xrpl/protocol/AccountID.h>
17#include <xrpl/protocol/Asset.h>
18#include <xrpl/protocol/Feature.h>
19#include <xrpl/protocol/SField.h>
20#include <xrpl/protocol/STAmount.h>
21#include <xrpl/protocol/TER.h>
22#include <xrpl/protocol/TxFlags.h>
23#include <xrpl/protocol/jss.h>
24
25#include <string>
26#include <tuple>
27#include <utility>
28
29namespace xrpl {
30
32{
33private:
34 void
36 {
37 using namespace test::jtx;
38 testcase("VaultDeposit IOU freeze checks");
39
40 Account const issuer{"issuer"};
41 Account const owner{"owner"};
42 Env env{*this};
43 Vault vault{env};
44
45 env.fund(XRP(100'000), issuer, owner);
46 env(fset(issuer, asfAllowTrustLineClawback));
47 env.close();
48 PrettyAsset const asset = issuer["IOU"];
49 env.trust(asset(1'000'000), owner);
50 env(pay(issuer, owner, asset(100'000)));
51 env.close();
52
53 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
54 env(tx);
55 env.close();
56 auto const vaultAcct = Account("vault", env.le(keylet)->at(sfAccount));
57
58 // Initial deposit so the vault pseudo-account has a trustline
59 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
60 env.close();
61
62 auto runTests = [&]() {
63 auto const fix330Enabled = env.current()->rules().enabled(fixCleanup3_3_0);
64
65 // Global freeze
66 {
67 testcase("VaultDeposit IOU global freeze");
68 env(fset(issuer, asfGlobalFreeze));
69 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
70 Ter(tecFROZEN));
71 env(fclear(issuer, asfGlobalFreeze));
72 }
73
74 // Depositor freeze
75 {
76 testcase("VaultDeposit IOU depositor freeze");
77 env(trust(issuer, asset(0), owner, tfSetFreeze));
78 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
79 Ter(tecFROZEN));
80 env(trust(issuer, asset(0), owner, tfClearFreeze));
81 }
82
83 // Depositor deep freeze
84 {
85 testcase("VaultDeposit IOU depositor deep freeze");
86 env(trust(issuer, asset(0), owner, tfSetFreeze | tfSetDeepFreeze));
87 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
88 Ter(tecFROZEN));
89 env(trust(issuer, asset(0), owner, tfClearFreeze | tfClearDeepFreeze));
90 }
91
92 // Vault-account freeze
93 // Post-fix: checkDepositFreeze catches it → tecFROZEN
94 // Pre-fix: not checked directly, but the transitive share
95 // check triggers → tecLOCKED
96 {
97 testcase("VaultDeposit IOU pseudo-account freeze");
98 auto trustSet = [&]() {
99 json::Value jv;
100 jv[jss::Account] = issuer.human();
101 {
102 auto& ja = jv[jss::LimitAmount] =
103 asset(0).value().getJson(JsonOptions::Values::None);
104 ja[jss::issuer] = toBase58(vaultAcct.id());
105 }
106 jv[jss::TransactionType] = jss::TrustSet;
107 return jv;
108 }();
109
110 trustSet[jss::Flags] = tfSetFreeze;
111 env(trustSet);
112 env.close();
113
114 TER const expected = fix330Enabled ? TER(tecFROZEN) : TER(tecLOCKED);
115 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
116 Ter(expected));
117
118 trustSet[jss::Flags] = tfClearFreeze;
119 env(trustSet);
120 env.close();
121 }
122
123 // Vault-account deep freeze
124 {
125 testcase("VaultDeposit IOU pseudo-account deep freeze");
126 auto trustSet = [&]() {
127 json::Value jv;
128 jv[jss::Account] = issuer.human();
129 {
130 auto& ja = jv[jss::LimitAmount] =
131 asset(0).value().getJson(JsonOptions::Values::None);
132 ja[jss::issuer] = toBase58(vaultAcct.id());
133 }
134 jv[jss::TransactionType] = jss::TrustSet;
135 return jv;
136 }();
137
138 trustSet[jss::Flags] = tfSetFreeze | tfSetDeepFreeze;
139 env(trustSet);
140 env.close();
141
142 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
143 Ter(fix330Enabled ? TER(tecFROZEN) : TER(tecLOCKED)));
144
145 trustSet[jss::Flags] = tfClearFreeze | tfClearDeepFreeze;
146 env(trustSet);
147 env.close();
148 }
149
150 // Clawback works while frozen
151 {
152 testcase("VaultDeposit IOU freeze clawback unaffected");
153 env(fset(issuer, asfGlobalFreeze));
154 env(vault.clawback(
155 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(1)}));
156 env(fclear(issuer, asfGlobalFreeze));
157 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
158 env.close();
159 }
160 };
161
162 runTests();
163 env.disableFeature(fixCleanup3_3_0);
164 runTests();
165 env.enableFeature(fixCleanup3_3_0);
166 }
167
168 void
170 {
171 using namespace test::jtx;
172 testcase("VaultDeposit MPT lock checks");
173
174 Account const issuer{"issuer"};
175 Account const owner{"owner"};
176 Env env{*this};
177 Vault vault{env};
178
179 env.fund(XRP(100'000), issuer, owner);
180 env.close();
181
182 MPTTester mptt{env, issuer, kMptInitNoFund};
183 mptt.create(
184 {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth});
185 PrettyAsset const mpt{mptt.issuanceID()};
186
187 mptt.authorize({.account = owner});
188 mptt.authorize({.account = issuer, .holder = owner});
189 env.close();
190 env(pay(issuer, owner, mpt(100'000)));
191 env.close();
192
193 auto [tx, keylet] = vault.create({.owner = owner, .asset = mpt});
194 env(tx);
195 env.close();
196 auto const vaultAcctID = env.le(keylet)->at(sfAccount);
197 Account const vaultAcct("vault", vaultAcctID);
198
199 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(100)}));
200 env.close();
201
202 // For MPT isDeepFrozen == isFrozen, so all locks block in
203 // both pre- and post-fix.
204 auto runTests = [&]() {
205 // Global lock
206 {
207 testcase("VaultDeposit MPT global lock");
208 mptt.set({.flags = tfMPTLock});
209 env.close();
210 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
211 Ter(tecLOCKED));
212 mptt.set({.flags = tfMPTUnlock});
213 env.close();
214 }
215
216 // Depositor individual lock
217 {
218 testcase("VaultDeposit MPT depositor lock");
219 mptt.set({.holder = owner, .flags = tfMPTLock});
220 env.close();
221 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
222 Ter(tecLOCKED));
223 mptt.set({.holder = owner, .flags = tfMPTUnlock});
224 env.close();
225 }
226
227 // Vault pseudo-account individual lock
228 {
229 testcase("VaultDeposit MPT pseudo-account lock");
230 mptt.set({.holder = vaultAcct, .flags = tfMPTLock});
231 env.close();
232 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
233 Ter(tecLOCKED));
234 mptt.set({.holder = vaultAcct, .flags = tfMPTUnlock});
235 env.close();
236 }
237
238 // Clawback works while locked
239 {
240 testcase("VaultDeposit MPT lock clawback unaffected");
241 mptt.set({.flags = tfMPTLock});
242 env.close();
243 env(vault.clawback(
244 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = mpt(1)}));
245 mptt.set({.flags = tfMPTUnlock});
246 env.close();
247 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
248 env.close();
249 }
250 };
251
252 runTests();
253 env.disableFeature(fixCleanup3_3_0);
254 runTests();
255 env.enableFeature(fixCleanup3_3_0);
256 }
257
258 void
260 {
261 using namespace test::jtx;
262 testcase("VaultWithdraw IOU freeze checks");
263
264 Account const issuer{"issuer"};
265 Account const owner{"owner"};
266 Env env{*this};
267 Vault const vault{env};
268
269 env.fund(XRP(100'000), issuer, owner);
270 env(fset(issuer, asfAllowTrustLineClawback));
271 env.close();
272 PrettyAsset const asset = issuer["IOU"];
273 env.trust(asset(1'000'000), owner);
274 env(pay(issuer, owner, asset(100'000)));
275 env.close();
276
277 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
278 env(tx);
279 env.close();
280 auto const vaultAcct = Account("vault", env.le(keylet)->at(sfAccount));
281
282 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
283 env.close();
284
285 Account const charlie{"charlie"};
286 env.fund(XRP(10'000), charlie);
287 env.trust(asset(1'000'000), charlie);
288 env.close();
289
290 auto runTests = [&]() {
291 auto const fix330Enabled = env.current()->rules().enabled(fixCleanup3_3_0);
292 // Global freeze → self-withdraw
293 {
294 testcase("VaultWithdraw IOU global freeze");
295 env(fset(issuer, asfGlobalFreeze));
296 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
297 Ter(tecFROZEN));
298 // Global freeze → withdraw to 3rd party
299
300 auto withdrawToCharlie =
301 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)});
302 withdrawToCharlie[sfDestination] = charlie.human();
303 env(withdrawToCharlie, Ter(tecFROZEN));
304
305 env(fclear(issuer, asfGlobalFreeze));
306 }
307
308 // Vault-account freeze
309 {
310 testcase("VaultWithdraw IOU pseudo-account freeze");
311 auto trustSet = [&]() {
312 json::Value jv;
313 jv[jss::Account] = issuer.human();
314 {
315 auto& ja = jv[jss::LimitAmount] =
316 asset(0).value().getJson(JsonOptions::Values::None);
317 ja[jss::issuer] = toBase58(vaultAcct.id());
318 }
319 jv[jss::TransactionType] = jss::TrustSet;
320 return jv;
321 }();
322
323 trustSet[jss::Flags] = tfSetFreeze;
324 env(trustSet);
325 env.close();
326
327 TER const terExpected = fix330Enabled ? TER(tecFROZEN) : TER(tecLOCKED);
328
329 // Self-withdraw
330 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
331 Ter(terExpected));
332 // Withdraw to 3rd party
333
334 auto withdrawToCharlie =
335 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)});
336 withdrawToCharlie[sfDestination] = charlie.human();
337 env(withdrawToCharlie, Ter(terExpected));
338
339 trustSet[jss::Flags] = tfClearFreeze;
340 env(trustSet);
341 env.close();
342 }
343
344 // Depositor freeze, self-withdraw
345 {
346 testcase("VaultWithdraw IOU self-withdraw freeze check");
347 env(trust(issuer, asset(0), owner, tfSetFreeze));
348
349 // Post-fix: self-withdraw allowed (submitter==dst skip)
350 // Pre-fix: isFrozen(depositor, iou) catches it
351 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
352 Ter(fix330Enabled ? TER(tesSUCCESS) : TER(tecFROZEN)));
353
354 // Depositor freeze withdraw to 3rd party
355 auto withdrawTo3rd =
356 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)});
357 withdrawTo3rd[sfDestination] = charlie.human();
358
359 // Post-fix: submitter freeze blocks withdraw to 3rd party
360 // Pre-fix: submitter's IOU freeze not checked, but checkFrozen(depositor,
361 // share) triggers tecLOCKED
362 env(withdrawTo3rd, Ter(fix330Enabled ? TER(tecFROZEN) : TER(tecLOCKED)));
363
364 env(trust(issuer, asset(0), owner, tfClearFreeze));
365 // Replenish what was withdrawn
366 if (fix330Enabled)
367 {
368 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
369 }
370 env.close();
371 }
372
373 // Depositor deep freeze → self-withdraw blocked
374 {
375 testcase("VaultWithdraw IOU depositor deep freeze");
376 env(trust(issuer, asset(0), owner, tfSetFreeze | tfSetDeepFreeze));
377
378 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
379 Ter(tecFROZEN));
380
381 env(trust(issuer, asset(0), owner, tfClearFreeze | tfClearDeepFreeze));
382 }
383
384 // Destination freeze → withdraw to 3rd party
385 {
386 testcase("VaultWithdraw IOU freeze withdraw to 3rd party");
387
388 env(trust(issuer, asset(0), charlie, tfSetFreeze));
389
390 // Self-withdraw unaffected by charlie's freeze
391 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
392
393 auto withdrawToCharlie =
394 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)});
395 withdrawToCharlie[sfDestination] = charlie.human();
396
397 // Post-fix: freeze on dst allowed
398 // Pre-fix: checkFrozen(dst, iou) catches it
399 env(withdrawToCharlie, Ter(fix330Enabled ? TER(tesSUCCESS) : TER(tecFROZEN)));
400
401 env(trust(issuer, asset(0), charlie, tfClearFreeze));
402
403 // Replenish: 1 for self-withdraw + 1 if charlie withdraw succeeded
404 env(vault.deposit(
405 {.depositor = owner,
406 .id = keylet.key,
407 .amount = asset(fix330Enabled ? 2 : 1)}));
408 env.close();
409 }
410
411 // Destination deep freeze → withdraw to 3rd party blocked
412 {
413 testcase("VaultWithdraw IOU deep freeze withdraw to 3rd party");
414
415 env(trust(issuer, asset(0), charlie, tfSetFreeze | tfSetDeepFreeze));
416
417 auto withdrawToCharlie =
418 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)});
419 withdrawToCharlie[sfDestination] = charlie.human();
420 env(withdrawToCharlie, Ter(tecFROZEN));
421
422 // Destination deep freeze → self-withdraw unaffected
423 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
424
425 env(trust(issuer, asset(0), charlie, tfClearFreeze | tfClearDeepFreeze));
426 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
427 env.close();
428 }
429
430 // Clawback works while frozen
431 {
432 testcase("VaultWithdraw IOU freeze clawback unaffected");
433 env(fset(issuer, asfGlobalFreeze));
434
435 env(vault.clawback(
436 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(1)}));
437
438 env(fclear(issuer, asfGlobalFreeze));
439 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
440 env.close();
441 }
442 };
443
444 runTests();
445 env.disableFeature(fixCleanup3_3_0);
446 runTests();
447 env.enableFeature(fixCleanup3_3_0);
448 }
449
450 void
452 {
453 using namespace test::jtx;
454 testcase("VaultWithdraw MPT lock checks");
455
456 Account const issuer{"issuer"};
457 Account const owner{"owner"};
458 Env env{*this};
459 Vault vault{env};
460
461 env.fund(XRP(100'000), issuer, owner);
462 env.close();
463
464 MPTTester mptt{env, issuer, kMptInitNoFund};
465 mptt.create(
466 {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth});
467 PrettyAsset const mpt{mptt.issuanceID()};
468
469 mptt.authorize({.account = owner});
470 mptt.authorize({.account = issuer, .holder = owner});
471 env.close();
472 env(pay(issuer, owner, mpt(100'000)));
473 env.close();
474
475 auto [tx, keylet] = vault.create({.owner = owner, .asset = mpt});
476 env(tx);
477 env.close();
478 Account const vaultAcct("vault", env.le(keylet)->at(sfAccount));
479
480 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(100)}));
481 env.close();
482
483 Account const charlie{"charlie"};
484 env.fund(XRP(10'000), charlie);
485 env.close();
486 mptt.authorize({.account = charlie});
487 mptt.authorize({.account = issuer, .holder = charlie});
488 env.close();
489
490 auto runTests = [&]() {
491 auto const fix330Enabled = env.current()->rules().enabled(fixCleanup3_3_0);
492
493 // Global lock
494 {
495 testcase("VaultWithdraw MPT global lock");
496 mptt.set({.flags = tfMPTLock});
497 env.close();
498 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
499 Ter(tecLOCKED));
500
501 // Global lock → withdraw to issuer
502 // Post-fix: bypasses freeze checks, but accountHolds
503 // on the pseudo returns 0 under global lock
504 // Pre-fix: checkFrozen(dst=issuer) catches global lock
505 {
506 auto withdrawToIssuer =
507 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)});
508 withdrawToIssuer[sfDestination] = issuer.human();
509 env(withdrawToIssuer, Ter(fix330Enabled ? TER(tesSUCCESS) : TER(tecLOCKED)));
510 }
511 mptt.set({.flags = tfMPTUnlock});
512 env.close();
513 if (fix330Enabled)
514 {
515 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
516 }
517 env.close();
518 }
519
520 // Vault pseudo-account individual lock
521 {
522 testcase("VaultWithdraw MPT pseudo-account lock");
523 mptt.set({.holder = vaultAcct, .flags = tfMPTLock});
524 env.close();
525 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
526 Ter(tecLOCKED));
527 mptt.set({.holder = vaultAcct, .flags = tfMPTUnlock});
528 env.close();
529 }
530
531 // Depositor individual lock → self-withdraw blocked
532 // (isDeepFrozen == isFrozen for MPT)
533 {
534 testcase("VaultWithdraw MPT depositor lock");
535 mptt.set({.holder = owner, .flags = tfMPTLock});
536 env.close();
537 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
538 Ter(tecLOCKED));
539 // Depositor lock → withdraw to 3rd party also blocked
540 {
541 auto withdrawToCharlie =
542 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)});
543 withdrawToCharlie[sfDestination] = charlie.human();
544 env(withdrawToCharlie, Ter(tecLOCKED));
545 }
546
547 // Depositor lock → withdraw to issuer
548 // Post-fix: issuer bypass in checkWithdrawFreezes
549 // Pre-fix: checkFrozen(depositor, share) blocks transitively
550 {
551 auto withdrawToIssuer =
552 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)});
553 withdrawToIssuer[sfDestination] = issuer.human();
554 env(withdrawToIssuer, Ter(fix330Enabled ? TER(tesSUCCESS) : TER(tecLOCKED)));
555 }
556 mptt.set({.holder = owner, .flags = tfMPTUnlock});
557 env.close();
558 if (fix330Enabled)
559 {
560 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
561 }
562 env.close();
563 }
564
565 // 3rd party destination lock → withdraw to 3rd party blocked
566 {
567 testcase("VaultWithdraw MPT 3rd party destination lock");
568 mptt.set({.holder = charlie, .flags = tfMPTLock});
569 env.close();
570 {
571 auto withdrawToCharlie =
572 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)});
573 withdrawToCharlie[sfDestination] = charlie.human();
574 env(withdrawToCharlie, Ter{tecLOCKED});
575 }
576 // 3rd party lock → self-withdraw unaffected
577 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
578 mptt.set({.holder = charlie, .flags = tfMPTUnlock});
579 env.close();
580 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
581 env.close();
582 }
583
584 // Clawback works while locked
585 {
586 testcase("VaultWithdraw MPT lock clawback unaffected");
587 mptt.set({.flags = tfMPTLock});
588 env.close();
589 env(vault.clawback(
590 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = mpt(1)}));
591 mptt.set({.flags = tfMPTUnlock});
592 env.close();
593 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
594 env.close();
595 }
596 };
597
598 runTests();
599 env.disableFeature(fixCleanup3_3_0);
600 runTests();
601 env.enableFeature(fixCleanup3_3_0);
602 }
603
604 // Focused demonstration: a depositor under an individual IOU freeze
605 // can still withdraw to themselves (self-withdrawal), but is blocked from
606 // withdrawing to a third party.
607 //
608 // Pre-fixCleanup3_3_0: both the self-withdrawal AND the third-party
609 // withdrawal were blocked because the old code checked checkFrozen on the
610 // destination regardless of whether it was the submitter.
611 // Post-fixCleanup3_3_0: checkWithdrawFreeze skips the submitter freeze
612 // check when submitter == destination, so self-withdrawal succeeds.
613 void
615 {
616 testcase("VaultWithdraw IOU self-withdrawal while individually frozen");
617
618 using namespace test::jtx;
619
620 Account const issuer{"issuer"};
621 Account const owner{"owner"};
622 Account const charlie{"charlie"};
623 Env env{*this};
624 Vault vault{env};
625
626 env.fund(XRP(100'000), issuer, owner, charlie);
627 env(fset(issuer, asfAllowTrustLineClawback));
628 env.close();
629
630 PrettyAsset const asset = issuer["IOU"];
631 env.trust(asset(1'000'000), owner);
632 env.trust(asset(1'000'000), charlie);
633 env(pay(issuer, owner, asset(100'000)));
634 env.close();
635
636 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
637 env(tx);
638 env.close();
639
640 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)}));
641 env.close();
642
643 auto runTests = [&]() {
644 auto const fix330Enabled = env.current()->rules().enabled(fixCleanup3_3_0);
645
646 // Set an individual freeze on the owner's IOU trustline.
647 env(trust(issuer, asset(0), owner, tfSetFreeze));
648 env.close();
649
650 // Self-withdrawal: submitter == destination, so the submitter
651 // freeze check is skipped.
652 // Post-fix: tesSUCCESS. Pre-fix: tecFROZEN.
653 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
654 Ter(fix330Enabled ? TER(tesSUCCESS) : TER(tecFROZEN)));
655
656 // Withdrawal to a third party is blocked: submitter != destination
657 // so the submitter freeze check applies.
658 {
659 auto withdrawToCharlie =
660 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)});
661 withdrawToCharlie[sfDestination] = charlie.human();
662 // Post-fix: tecFROZEN (checkIndividualFrozen on submitter).
663 // Pre-fix: tecLOCKED (isFrozen on the vault share).
664 env(withdrawToCharlie, Ter(fix330Enabled ? TER(tecFROZEN) : TER(tecLOCKED)));
665 }
666
667 env(trust(issuer, asset(0), owner, tfClearFreeze));
668 env.close();
669 };
670
671 runTests();
672 env.disableFeature(fixCleanup3_3_0);
673 runTests();
674 env.enableFeature(fixCleanup3_3_0);
675 }
676
677public:
678 void
687};
688
689BEAST_DEFINE_TESTSUITE(VaultFreeze, app, xrpl);
690
691} // namespace xrpl
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Represents a JSON value.
Definition json_value.h:117
void run() override
Runs the suite.
Shared base for the Vault*_test family under src/test/app/vault/.
test::jtx::PrettyAsset PrettyAsset
Keylet computation functions.
Definition Indexes.h:40
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
TERSubset< CanCvtToTER > TER
Definition TER.h:654
@ tecLOCKED
Definition TER.h:366
@ tecFROZEN
Definition TER.h:311
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
@ tesSUCCESS
Definition TER.h:250