xrpld
Loading...
Searching...
No Matches
VaultValidation_test.cpp
1#include <test/app/vault/VaultTestBase.h>
2#include <test/jtx/AMM.h>
3#include <test/jtx/AMMTest.h>
4#include <test/jtx/Account.h>
5#include <test/jtx/Env.h>
6#include <test/jtx/TestHelpers.h>
7#include <test/jtx/amount.h>
8#include <test/jtx/credentials.h>
9#include <test/jtx/fee.h>
10#include <test/jtx/flags.h>
11#include <test/jtx/mpt.h>
12#include <test/jtx/pay.h>
13#include <test/jtx/permissioned_domains.h>
14#include <test/jtx/ter.h>
15#include <test/jtx/trust.h>
16#include <test/jtx/vault.h>
17
18#include <xrpl/basics/Number.h>
19#include <xrpl/basics/base_uint.h>
20#include <xrpl/basics/strHex.h>
21#include <xrpl/beast/unit_test/suite.h>
22#include <xrpl/beast/utility/Zero.h>
23#include <xrpl/json/json_forwards.h>
24#include <xrpl/json/json_value.h>
25#include <xrpl/protocol/Asset.h>
26#include <xrpl/protocol/Feature.h>
27#include <xrpl/protocol/Indexes.h>
28#include <xrpl/protocol/Issue.h>
29#include <xrpl/protocol/Keylet.h>
30#include <xrpl/protocol/Protocol.h>
31#include <xrpl/protocol/SField.h>
32#include <xrpl/protocol/STAmount.h>
33#include <xrpl/protocol/SeqProxy.h>
34#include <xrpl/protocol/TER.h>
35#include <xrpl/protocol/TxFlags.h>
36#include <xrpl/protocol/jss.h>
37
38#include <functional>
39#include <string>
40#include <tuple>
41#include <utility>
42
43namespace xrpl {
44
46{
47private:
48 void
50 {
51 using namespace test::jtx;
52
53 struct CaseArgs
54 {
55 FeatureBitset features = testableAmendments();
56 };
57
58 auto testCase = [&, this](
59 std::function<void(
60 Env & env,
61 Account const& issuer,
62 Account const& owner,
63 Asset const& asset,
64 Vault& vault)> test,
65 CaseArgs args = {}) {
66 Env env{*this, args.features};
67 Account const issuer{"issuer"};
68 Account const owner{"owner"};
69 Vault vault{env};
70 env.fund(XRP(1000), issuer, owner);
71 env.close();
72
73 env(fset(issuer, asfAllowTrustLineClawback));
74 env(fset(issuer, asfRequireAuth));
75 env.close();
76
77 PrettyAsset const asset = issuer["IOU"];
78 env(trust(owner, asset(1000)));
79 env(trust(issuer, asset(0), owner, tfSetfAuth));
80 env(pay(issuer, owner, asset(1000)));
81 env.close();
82
83 test(env, issuer, owner, asset, vault);
84 };
85
86 auto testDisabled = [&](TER resultAfterCreate = temDISABLED) {
87 return [&, resultAfterCreate](
88 Env& env,
89 Account const& issuer,
90 Account const& owner,
91 Asset const& asset,
92 Vault& vault) {
93 testcase("disabled single asset vault");
94
95 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
96 env(tx, Ter{temDISABLED});
97
98 {
99 auto tx = vault.set({.owner = owner, .id = keylet.key});
100 env(tx, kData("test"), Ter{resultAfterCreate});
101 }
102
103 {
104 auto tx =
105 vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)});
106 env(tx, Ter{resultAfterCreate});
107 }
108
109 {
110 auto tx =
111 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
112 env(tx, Ter{resultAfterCreate});
113 }
114
115 {
116 auto tx = vault.clawback(
117 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(10)});
118 env(tx, Ter{resultAfterCreate});
119 }
120
121 {
122 auto tx = vault.del({.owner = owner, .id = keylet.key});
123 env(tx, Ter{resultAfterCreate});
124 }
125 };
126 };
127
128 testCase(testDisabled(), {.features = testableAmendments() - featureSingleAssetVault});
129
130 testCase(testDisabled(tecNO_ENTRY), {.features = testableAmendments() - featureMPTokensV1});
131
132 testCase(
133 [&](Env& env,
134 Account const& issuer,
135 Account const& owner,
136 Asset const& asset,
137 Vault& vault) {
138 testcase("disabled permissioned domains");
139
140 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
141 env(tx);
142
143 tx[sfFlags] = tx[sfFlags].asUInt() | tfVaultPrivate;
144 tx[sfDomainID] = to_string(BaseUInt<256>(42ul));
145 env(tx, Ter{temDISABLED});
146
147 {
148 auto tx = vault.set({.owner = owner, .id = keylet.key});
149 env(tx, kData("Test"));
150
151 tx[sfDomainID] = to_string(BaseUInt<256>(13ul));
152 env(tx, Ter{temDISABLED});
153 }
154 },
155 {.features = testableAmendments() - featurePermissionedDomains});
156
157 testCase([&](Env& env,
158 Account const& issuer,
159 Account const& owner,
160 Asset const& asset,
161 Vault& vault) {
162 testcase("invalid flags");
163
164 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
165 tx[sfFlags] = tfClearDeepFreeze;
166 env(tx, Ter{temINVALID_FLAG});
167
168 {
169 auto tx = vault.set({.owner = owner, .id = keylet.key});
170 tx[sfFlags] = tfClearDeepFreeze;
171 env(tx, Ter{temINVALID_FLAG});
172 }
173
174 {
175 auto tx =
176 vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)});
177 tx[sfFlags] = tfClearDeepFreeze;
178 env(tx, Ter{temINVALID_FLAG});
179 }
180
181 {
182 auto tx =
183 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
184 tx[sfFlags] = tfClearDeepFreeze;
185 env(tx, Ter{temINVALID_FLAG});
186 }
187
188 {
189 auto tx = vault.clawback(
190 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(10)});
191 tx[sfFlags] = tfClearDeepFreeze;
192 env(tx, Ter{temINVALID_FLAG});
193 }
194
195 {
196 auto tx = vault.del({.owner = owner, .id = keylet.key});
197 tx[sfFlags] = tfClearDeepFreeze;
198 env(tx, Ter{temINVALID_FLAG});
199 }
200 });
201
202 testCase([&](Env& env,
203 Account const& issuer,
204 Account const& owner,
205 Asset const& asset,
206 Vault& vault) {
207 testcase("invalid fee");
208
209 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
210 tx[jss::Fee] = "-1";
211 env(tx, Ter{temBAD_FEE});
212
213 {
214 auto tx = vault.set({.owner = owner, .id = keylet.key});
215 tx[jss::Fee] = "-1";
216 env(tx, Ter{temBAD_FEE});
217 }
218
219 {
220 auto tx =
221 vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)});
222 tx[jss::Fee] = "-1";
223 env(tx, Ter{temBAD_FEE});
224 }
225
226 {
227 auto tx =
228 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
229 tx[jss::Fee] = "-1";
230 env(tx, Ter{temBAD_FEE});
231 }
232
233 {
234 auto tx = vault.clawback(
235 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(10)});
236 tx[jss::Fee] = "-1";
237 env(tx, Ter{temBAD_FEE});
238 }
239
240 {
241 auto tx = vault.del({.owner = owner, .id = keylet.key});
242 tx[jss::Fee] = "-1";
243 env(tx, Ter{temBAD_FEE});
244 }
245 });
246
247 testCase(
248 [&](Env& env, Account const&, Account const& owner, Asset const&, Vault& vault) {
249 testcase("disabled permissioned domain");
250
251 auto [tx, keylet] = vault.create({.owner = owner, .asset = xrpIssue()});
252 tx[sfDomainID] = to_string(BaseUInt<256>(42ul));
253 env(tx, Ter{temDISABLED});
254
255 {
256 auto tx = vault.set({.owner = owner, .id = keylet.key});
257 tx[sfDomainID] = to_string(BaseUInt<256>(42ul));
258 env(tx, Ter{temDISABLED});
259 }
260
261 {
262 auto tx = vault.set({.owner = owner, .id = keylet.key});
263 tx[sfDomainID] = "0";
264 env(tx, Ter{temDISABLED});
265 }
266 },
267 {.features = (testableAmendments()) - featurePermissionedDomains});
268
269 testCase([&](Env& env,
270 Account const& issuer,
271 Account const& owner,
272 Asset const& asset,
273 Vault& vault) {
274 testcase("use zero vault");
275
276 auto [tx, keylet] = vault.create({.owner = owner, .asset = xrpIssue()});
277
278 {
279 auto tx = vault.set({
280 .owner = owner,
281 .id = beast::kZero,
282 });
283 env(tx, Ter{temMALFORMED});
284 }
285
286 {
287 auto tx =
288 vault.deposit({.depositor = owner, .id = beast::kZero, .amount = asset(10)});
289 env(tx, Ter(temMALFORMED));
290 }
291
292 {
293 auto tx =
294 vault.withdraw({.depositor = owner, .id = beast::kZero, .amount = asset(10)});
295 env(tx, Ter{temMALFORMED});
296 }
297
298 {
299 auto tx = vault.clawback(
300 {.issuer = issuer, .id = beast::kZero, .holder = owner, .amount = asset(10)});
301 env(tx, Ter{temMALFORMED});
302 }
303
304 {
305 auto tx = vault.del({
306 .owner = owner,
307 .id = beast::kZero,
308 });
309 env(tx, Ter{temMALFORMED});
310 }
311 });
312
313 testCase(
314 [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) {
315 testcase("withdraw to bad destination");
316
317 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
318
319 {
320 auto tx =
321 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
322 tx[jss::Destination] = "0";
323 env(tx, Ter{temMALFORMED});
324 }
325 });
326
327 testCase(
328 [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) {
329 testcase("create with Scale");
330
331 {
332 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
333 tx[sfScale] = 255;
334 env(tx, Ter(temMALFORMED));
335 }
336
337 {
338 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
339 tx[sfScale] = 19;
340 env(tx, Ter(temMALFORMED));
341 }
342
343 // accepted range from 0 to 18
344 {
345 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
346 tx[sfScale] = 18;
347 env(tx);
348 env.close();
349 auto const sleVault = env.le(keylet);
350 BEAST_EXPECT(sleVault);
351 BEAST_EXPECT((*sleVault)[sfScale] == 18);
352 }
353
354 {
355 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
356 tx[sfScale] = 0;
357 env(tx);
358 env.close();
359 auto const sleVault = env.le(keylet);
360 BEAST_EXPECT(sleVault);
361 BEAST_EXPECT((*sleVault)[sfScale] == 0);
362 }
363
364 {
365 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
366 env(tx);
367 env.close();
368 auto const sleVault = env.le(keylet);
369 BEAST_EXPECT(sleVault);
370 BEAST_EXPECT((*sleVault)[sfScale] == 6);
371 }
372 });
373
374 testCase(
375 [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) {
376 testcase("create or set invalid data");
377
378 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
379
380 {
381 auto tx = tx1;
382 tx[sfData] = "";
383 env(tx, Ter(temMALFORMED));
384 }
385
386 {
387 auto tx = tx1;
388 // A hexadecimal string of 257 bytes.
389 tx[sfData] = std::string(514, 'A');
390 env(tx, Ter(temMALFORMED));
391 }
392
393 {
394 auto tx = vault.set({.owner = owner, .id = keylet.key});
395 tx[sfData] = "";
396 env(tx, Ter{temMALFORMED});
397 }
398
399 {
400 auto tx = vault.set({.owner = owner, .id = keylet.key});
401 // A hexadecimal string of 257 bytes.
402 tx[sfData] = std::string(514, 'A');
403 env(tx, Ter{temMALFORMED});
404 }
405 });
406
407 testCase(
408 [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) {
409 testcase("set nothing updated");
410
411 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
412
413 {
414 auto tx = vault.set({.owner = owner, .id = keylet.key});
415 env(tx, Ter{temMALFORMED});
416 }
417 });
418
419 testCase(
420 [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) {
421 testcase("create with invalid metadata");
422
423 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
424
425 {
426 auto tx = tx1;
427 tx[sfMPTokenMetadata] = "";
428 env(tx, Ter(temMALFORMED));
429 }
430
431 {
432 auto tx = tx1;
433 // This metadata is for the share token.
434 // A hexadecimal string of 1025 bytes.
435 tx[sfMPTokenMetadata] = std::string(2050, 'B');
436 env(tx, Ter(temMALFORMED));
437 }
438 });
439
440 testCase(
441 [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) {
442 testcase("set negative maximum");
443
444 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
445
446 {
447 auto tx = vault.set({.owner = owner, .id = keylet.key});
448 tx[sfAssetsMaximum] = kNegativeAmount(asset).number();
449 env(tx, Ter{temMALFORMED});
450 }
451 });
452
453 testCase(
454 [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) {
455 testcase("invalid deposit amount");
456
457 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
458
459 {
460 auto tx = vault.deposit(
461 {.depositor = owner, .id = keylet.key, .amount = kNegativeAmount(asset)});
462 env(tx, Ter(temBAD_AMOUNT));
463 }
464
465 {
466 auto tx =
467 vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(0)});
468 env(tx, Ter(temBAD_AMOUNT));
469 }
470 });
471
472 testCase(
473 [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) {
474 testcase("invalid set immutable flag");
475
476 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
477
478 {
479 auto tx = vault.set({.owner = owner, .id = keylet.key});
480 tx[sfFlags] = tfVaultPrivate;
481 env(tx, Ter(temINVALID_FLAG));
482 }
483 });
484
485 testCase(
486 [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) {
487 testcase("invalid withdraw amount");
488
489 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
490
491 {
492 auto tx = vault.withdraw(
493 {.depositor = owner, .id = keylet.key, .amount = kNegativeAmount(asset)});
494 env(tx, Ter(temBAD_AMOUNT));
495 }
496
497 {
498 auto tx =
499 vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(0)});
500 env(tx, Ter(temBAD_AMOUNT));
501 }
502 });
503
504 testCase([&](Env& env,
505 Account const& issuer,
506 Account const& owner,
507 Asset const& asset,
508 Vault& vault) {
509 testcase("invalid clawback");
510
511 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
512
513 // Preclaim only checks for native assets.
514 if (asset.native())
515 {
516 auto tx = vault.clawback(
517 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(50)});
518 env(tx, Ter(temMALFORMED));
519 }
520
521 {
522 auto tx = vault.clawback(
523 {.issuer = issuer,
524 .id = keylet.key,
525 .holder = owner,
526 .amount = kNegativeAmount(asset)});
527 env(tx, Ter(temBAD_AMOUNT));
528 }
529 });
530
531 testCase(
532 [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) {
533 testcase("invalid create");
534
535 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
536
537 {
538 auto tx = tx1;
539 tx[sfWithdrawalPolicy] = 0;
540 env(tx, Ter(temMALFORMED));
541 }
542
543 {
544 auto tx = tx1;
545 tx[sfDomainID] = to_string(BaseUInt<256>(42ul));
546 env(tx, Ter{temMALFORMED});
547 }
548
549 {
550 auto tx = tx1;
551 tx[sfAssetsMaximum] = kNegativeAmount(asset).number();
552 env(tx, Ter{temMALFORMED});
553 }
554
555 {
556 auto tx = tx1;
557 tx[sfFlags] = tfVaultPrivate;
558 tx[sfDomainID] = "0";
559 env(tx, Ter{temMALFORMED});
560 }
561 });
562 }
563
564 // Test for non-asset specific behaviors.
565 void
567 {
568 using namespace test::jtx;
569
570 auto testCase = [this](
571 std::function<void(
572 Env & env,
573 Account const& issuer,
574 Account const& owner,
575 Account const& depositor,
576 Asset const& asset,
577 Vault& vault)> test) {
578 Env env{*this, testableAmendments()};
579 Account const issuer{"issuer"};
580 Account const owner{"owner"};
581 Account const depositor{"depositor"};
582
583 env.fund(XRP(1000), issuer, owner, depositor);
584 env.close();
585 Vault vault{env};
586 Asset const asset = xrpIssue();
587
588 test(env, issuer, owner, depositor, asset, vault);
589 };
590
591 testCase([this](
592 Env& env,
593 Account const& issuer,
594 Account const& owner,
595 Account const& depositor,
596 PrettyAsset const& asset,
597 Vault& vault) {
598 testcase("nothing to set");
599 auto tx = vault.set({.owner = owner, .id = keylet::skip().key});
600 tx[sfAssetsMaximum] = asset(0).number();
601 env(tx, Ter(tecNO_ENTRY));
602 });
603
604 testCase([this](
605 Env& env,
606 Account const& issuer,
607 Account const& owner,
608 Account const& depositor,
609 PrettyAsset const& asset,
610 Vault& vault) {
611 testcase("nothing to deposit to");
612 auto tx = vault.deposit(
613 {.depositor = depositor, .id = keylet::skip().key, .amount = asset(10)});
614 env(tx, Ter(tecNO_ENTRY));
615 });
616
617 testCase([this](
618 Env& env,
619 Account const& issuer,
620 Account const& owner,
621 Account const& depositor,
622 PrettyAsset const& asset,
623 Vault& vault) {
624 testcase("nothing to withdraw from");
625 auto tx = vault.withdraw(
626 {.depositor = depositor, .id = keylet::skip().key, .amount = asset(10)});
627 env(tx, Ter(tecNO_ENTRY));
628 });
629
630 testCase([this](
631 Env& env,
632 Account const& issuer,
633 Account const& owner,
634 Account const& depositor,
635 Asset const& asset,
636 Vault& vault) {
637 testcase("nothing to delete");
638 auto tx = vault.del({.owner = owner, .id = keylet::skip().key});
639 env(tx, Ter(tecNO_ENTRY));
640 });
641
642 testCase([this](
643 Env& env,
644 Account const& issuer,
645 Account const& owner,
646 Account const& depositor,
647 Asset const& asset,
648 Vault& vault) {
649 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
650 testcase("transaction is good");
651 env(tx);
652 });
653
654 testCase([this](
655 Env& env,
656 Account const& issuer,
657 Account const& owner,
658 Account const& depositor,
659 Asset const& asset,
660 Vault& vault) {
661 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
662 tx[sfWithdrawalPolicy] = 1;
663 testcase("explicitly select withdrawal policy");
664 env(tx);
665 });
666
667 testCase([this](
668 Env& env,
669 Account const& issuer,
670 Account const& owner,
671 Account const& depositor,
672 Asset const& asset,
673 Vault& vault) {
674 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
675 testcase("insufficient fee");
676 env(tx, Fee(env.current()->fees().base - 1), Ter(telINSUF_FEE_P));
677 });
678
679 testCase([this](
680 Env& env,
681 Account const& issuer,
682 Account const& owner,
683 Account const& depositor,
684 Asset const& asset,
685 Vault& vault) {
686 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
687 testcase("insufficient reserve");
688 // It is possible to construct a complicated mathematical
689 // expression for this amount, but it is sadly not easy.
690 env(pay(owner, issuer, XRP(775)));
691 env.close();
692 env(tx, Ter(tecINSUFFICIENT_RESERVE));
693 });
694
695 testCase([this](
696 Env& env,
697 Account const& issuer,
698 Account const& owner,
699 Account const& depositor,
700 Asset const& asset,
701 Vault& vault) {
702 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
703 tx[sfFlags] = tfVaultPrivate;
704 tx[sfDomainID] = to_string(BaseUInt<256>(42ul));
705 testcase("non-existing domain");
706 env(tx, Ter{tecOBJECT_NOT_FOUND});
707 });
708
709 testCase([this](
710 Env& env,
711 Account const& issuer,
712 Account const& owner,
713 Account const& depositor,
714 Asset const& asset,
715 Vault& vault) {
716 testcase("cannot set Scale=0");
717 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
718 tx[sfScale] = 0;
719 env(tx, Ter{temMALFORMED});
720 });
721
722 testCase([this](
723 Env& env,
724 Account const& issuer,
725 Account const& owner,
726 Account const& depositor,
727 Asset const& asset,
728 Vault& vault) {
729 testcase("cannot set Scale=1");
730 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
731 tx[sfScale] = 1;
732 env(tx, Ter{temMALFORMED});
733 });
734 }
735
736 void
738 {
739 using namespace test::jtx;
740 {
741 {
742 testcase("IOU fail because MPT is disabled");
743 Env env{*this, (testableAmendments() - featureMPTokensV1)};
744 Account const issuer{"issuer"};
745 Account const owner{"owner"};
746 env.fund(XRP(1000), issuer, owner);
747 env.close();
748
749 Vault const vault{env};
750 Asset const asset = issuer["IOU"].asset();
751 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
752
753 env(tx, Ter(temDISABLED));
754 env.close();
755 }
756
757 {
758 testcase("IOU fail create frozen");
759 Env env{*this, testableAmendments()};
760 Account const issuer{"issuer"};
761 Account const owner{"owner"};
762 env.fund(XRP(1000), issuer, owner);
763 env.close();
764 env(fset(issuer, asfGlobalFreeze));
765 env.close();
766
767 Vault const vault{env};
768 Asset const asset = issuer["IOU"].asset();
769 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
770
771 env(tx, Ter(tecFROZEN));
772 env.close();
773 }
774
775 {
776 testcase("IOU fail create no ripling");
777 Env env{*this, testableAmendments()};
778 Account const issuer{"issuer"};
779 Account const owner{"owner"};
780 env.fund(XRP(1000), issuer, owner);
781 env.close();
782 env(fclear(issuer, asfDefaultRipple));
783 env.close();
784
785 Vault const vault{env};
786 Asset const asset = issuer["IOU"].asset();
787 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
788 env(tx, Ter(terNO_RIPPLE));
789 env.close();
790 }
791
792 {
793 testcase("IOU no issuer");
794 Env env{*this, testableAmendments()};
795 Account const issuer{"issuer"};
796 Account const owner{"owner"};
797 env.fund(XRP(1000), owner);
798 env.close();
799
800 Vault const vault{env};
801 Asset const asset = issuer["IOU"].asset();
802 {
803 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
804 env(tx, Ter(terNO_ACCOUNT));
805 env.close();
806 }
807 }
808 }
809
810 {
811 testcase("IOU fail create vault for AMM LPToken");
812 Env env{*this, testableAmendments()};
813 Account const gw("gateway");
814 Account const alice("alice");
815 Account const carol("carol");
816 IOU const usd = gw["USD"];
817
818 auto const [asset1, asset2] = std::pair<STAmount, STAmount>(XRP(10000), usd(10000));
819 auto toFund = [&](STAmount const& a) -> STAmount {
820 if (a.native())
821 {
822 auto const defXRP = XRP(30000);
823 if (a <= defXRP)
824 return defXRP;
825 return a + XRP(1000);
826 }
827 auto defIOU = STAmount{a.asset(), 30000};
828 if (a <= defIOU)
829 return defIOU;
830 return a + STAmount{a.asset(), 1000};
831 };
832 auto const toFund1 = toFund(asset1);
833 auto const toFund2 = toFund(asset2);
834 BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2);
835
836 if (!asset1.native() && !asset2.native())
837 {
838 fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All);
839 }
840 else if (asset1.native())
841 {
842 fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All);
843 }
844 else if (asset2.native())
845 {
846 fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All);
847 }
848
849 AMM const ammAlice(env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0});
850
851 Account const owner{"owner"};
852 env.fund(XRP(1000000), owner);
853
854 Vault const vault{env};
855 auto [tx, k] = vault.create({.owner = owner, .asset = ammAlice.lptIssue()});
856 env(tx, Ter{tecWRONG_ASSET});
857 env.close();
858 }
859 }
860
861 void
863 {
864 using namespace test::jtx;
865
866 auto testCase = [this](
867 std::function<void(
868 Env & env,
869 Account const& issuer,
870 Account const& owner,
871 Account const& depositor,
872 Asset const& asset,
873 Vault& vault)> test) {
874 Env env{*this, testableAmendments()};
875 Account const issuer{"issuer"};
876 Account const owner{"owner"};
877 Account const depositor{"depositor"};
878 env.fund(XRP(1000), issuer, owner, depositor);
879 env.close();
880 Vault vault{env};
881 MPTTester mptt{env, issuer, kMptInitNoFund};
882 // Locked because that is the default flag.
883 mptt.create();
884 Asset const asset = mptt.issuanceID();
885
886 test(env, issuer, owner, depositor, asset, vault);
887 };
888
889 testCase([this](
890 Env& env,
891 Account const& issuer,
892 Account const& owner,
893 Account const& depositor,
894 Asset const& asset,
895 Vault& vault) {
896 testcase("MPT no authorization");
897 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
898 env(tx, Ter(tecNO_AUTH));
899 });
900
901 testCase([this](
902 Env& env,
903 Account const& issuer,
904 Account const& owner,
905 Account const& depositor,
906 Asset const& asset,
907 Vault& vault) {
908 testcase("MPT cannot set Scale=0");
909 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
910 tx[sfScale] = 0;
911 env(tx, Ter{temMALFORMED});
912 });
913
914 testCase([this](
915 Env& env,
916 Account const& issuer,
917 Account const& owner,
918 Account const& depositor,
919 Asset const& asset,
920 Vault& vault) {
921 testcase("MPT cannot set Scale=1");
922 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
923 tx[sfScale] = 1;
924 env(tx, Ter{temMALFORMED});
925 });
926 }
927
928 void
930 {
931 using namespace test::jtx;
932
933 Env env{*this};
934
935 Account const owner{"owner"};
936 env.fund(XRP(1'000'000), owner);
937 env.close();
938
939 Vault const vault{env};
940
941 auto const keylet = keylet::vault(owner.id(), SeqProxy::rawSequence(1));
942 auto delTx = vault.del({.owner = owner, .id = keylet.key});
943
944 // Test VaultDelete with featureLendingProtocolV1_1 disabled
945 // Transaction fails if the data field is provided
946 {
947 testcase("VaultDelete memo data featureLendingProtocolV1_1 disabled");
948 env.disableFeature(featureLendingProtocolV1_1);
949 delTx[sfMemoData] = strHex(std::string(kMaxDataPayloadLength, 'A'));
950 env(delTx, Ter(temDISABLED));
951 env.enableFeature(featureLendingProtocolV1_1);
952 env.close();
953 }
954
955 // Transaction fails if the data field is too large
956 {
957 testcase("VaultDelete memo data featureLendingProtocolV1_1 enabled data too large");
958 delTx[sfMemoData] = strHex(std::string(kMaxDataPayloadLength + 1, 'A'));
959 env(delTx, Ter(temMALFORMED));
960 env.close();
961 }
962
963 // Transaction fails if the data field is set, but is empty
964 {
965 testcase("VaultDelete memo data featureLendingProtocolV1_1 enabled data empty");
966 delTx[sfMemoData] = strHex(std::string());
967 env(delTx, Ter(temMALFORMED));
968 env.close();
969 }
970
971 {
972 testcase("VaultDelete memo data featureLendingProtocolV1_1 enabled no vault");
973 auto const keylet = keylet::vault(owner.id(), SeqProxy::rawSequence(env.seq(owner)));
974
975 // Recreate the transaction as the vault keylet changed
976 auto delTx = vault.del({.owner = owner, .id = keylet.key});
977 delTx[sfMemoData] = strHex(std::string(kMaxDataPayloadLength, 'A'));
978 env(delTx, Ter(tecNO_ENTRY));
979 env.close();
980 }
981
982 {
983 testcase("VaultDelete memo data featureLendingProtocolV1_1 enabled data valid");
984 PrettyAsset const xrpAsset = xrpIssue();
985 auto const [tx, keylet] = vault.create({.owner = owner, .asset = xrpAsset});
986 env(tx, Ter(tesSUCCESS));
987 env.close();
988 // Recreate the transaction as the vault keylet changed
989 auto delTx = vault.del({.owner = owner, .id = keylet.key});
990 delTx[sfMemoData] = strHex(std::string(kMaxDataPayloadLength, 'A'));
991 env(delTx, Ter(tesSUCCESS));
992 env.close();
993 }
994 }
995
996 void
998 {
999 using namespace test::jtx;
1000
1001 Account const owner{"owner"};
1002 PrettyAsset const xrpAsset = xrpIssue();
1003
1004 {
1005 testcase("VaultCreate LEVersion: featureLendingProtocolV1_1 disabled, field absent");
1006 Env env{*this};
1007 env.disableFeature(featureLendingProtocolV1_1);
1008 env.fund(XRP(1'000'000), owner);
1009 env.close();
1010
1011 Vault const vault{env};
1012 auto const [tx, keylet] = vault.create({.owner = owner, .asset = xrpAsset});
1013 env(tx, Ter(tesSUCCESS));
1014 env.close();
1015
1016 auto const sleVault = env.le(keylet);
1017 BEAST_EXPECT(sleVault);
1018 BEAST_EXPECT(!sleVault->isFieldPresent(sfLEVersion));
1019 }
1020
1021 {
1022 testcase(
1023 "VaultCreate LEVersion: featureLendingProtocolV1_1 enabled, LEVersion == "
1024 "VaultVersion::CashBasis");
1025 Env env{*this};
1026 env.fund(XRP(1'000'000), owner);
1027 env.close();
1028
1029 Vault const vault{env};
1030 auto const [tx, keylet] = vault.create({.owner = owner, .asset = xrpAsset});
1031 env(tx, Ter(tesSUCCESS));
1032 env.close();
1033
1034 auto const sleVault = env.le(keylet);
1035 BEAST_EXPECT(sleVault);
1036 BEAST_EXPECT(sleVault->isFieldPresent(sfLEVersion));
1037 BEAST_EXPECT(sleVault->at(sfLEVersion) == std::to_underlying(VaultVersion::CashBasis));
1038 }
1039
1040 {
1041 testcase("VaultCreate rejects LEVersion set in the transaction");
1042 Env env{*this};
1043 env.fund(XRP(1'000'000), owner);
1044 env.close();
1045
1046 Vault const vault{env};
1047 auto [tx, keylet] = vault.create({.owner = owner, .asset = xrpAsset});
1048 tx[sfLEVersion] = 2;
1049 env(tx, Ter(temMALFORMED));
1050 env.close();
1051
1052 BEAST_EXPECT(!env.le(keylet));
1053 }
1054
1055 {
1056 testcase("VaultSet rejects LEVersion set in the transaction");
1057 Env env{*this};
1058 env.fund(XRP(1'000'000), owner);
1059 env.close();
1060
1061 Vault const vault{env};
1062 auto const [createTx, keylet] = vault.create({.owner = owner, .asset = xrpAsset});
1063 env(createTx, Ter(tesSUCCESS));
1064 env.close();
1065
1066 auto setTx = vault.set({.owner = owner, .id = keylet.key});
1067 setTx[sfLEVersion] = 2;
1068 env(setTx, Ter(temMALFORMED));
1069 env.close();
1070 }
1071 }
1072
1073 // A pseudo-account belongs to a ledger object, so it must never be the
1074 // destination of a withdrawal. The payout is refused either way, by the
1075 // deposit authorization every pseudo-account carries, so the only change
1076 // is a misleading tecNO_PERMISSION becoming tecPSEUDO_ACCOUNT. The check
1077 // runs ahead of the private-vault domain check, which would otherwise
1078 // report a domain problem against an account that can never join one.
1079 void
1081 {
1082 using namespace test::jtx;
1083
1084 bool const withFix = features[fixCleanup3_4_0];
1085 testcase(
1086 std::string{"VaultWithdraw pseudo-account destination"} +
1087 (withFix ? " (fixCleanup3_4_0)" : " (pre-fix)"));
1088
1089 Account const issuer{"issuer"};
1090 Account const owner{"owner"};
1091 Account const depositor{"depositor"};
1092 Account const pdOwner{"pdOwner"};
1093 Account const credIssuer{"credIssuer"};
1094 std::string const credType = "credential";
1095
1096 Env env{*this, features};
1097 Vault const vault{env};
1098
1099 env.fund(XRP(100'000), issuer, owner, depositor, pdOwner, credIssuer);
1100 // Rippling plays no part in what is being tested here, and would
1101 // otherwise stop the payout before it reaches the check under test.
1102 env(fset(issuer, asfDefaultRipple));
1103 env.close();
1104
1105 PrettyAsset const asset = issuer["IOU"];
1106 for (auto const& account : {owner, depositor})
1107 {
1108 env.trust(asset(1'000'000), account);
1109 env(pay(issuer, account, asset(10'000)));
1110 }
1111 env.close();
1112
1113 // Another vault over the same asset supplies the destination. Its
1114 // pseudo-account holds a trust line for the asset from creation, so
1115 // the payout is refused for being a pseudo-account and nothing else.
1116 auto const pseudoDestination = [&]() {
1117 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1118 env(tx);
1119 env.close();
1120 return Account("otherVault", env.le(keylet)->at(sfAccount));
1121 }();
1122
1123 TER const expected = withFix ? TER(tecPSEUDO_ACCOUNT) : TER(tecNO_PERMISSION);
1124
1125 auto const withdrawToPseudo = [&](UInt256 const& vaultId) {
1126 auto tx = vault.withdraw({.depositor = depositor, .id = vaultId, .amount = asset(1)});
1127 tx[sfDestination] = pseudoDestination.human();
1128 return tx;
1129 };
1130
1131 {
1132 auto [createTx, keylet] = vault.create({.owner = owner, .asset = asset});
1133 env(createTx);
1134 env.close();
1135
1136 env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1'000)}));
1137 env.close();
1138
1139 env(withdrawToPseudo(keylet.key), Ter(expected));
1140 env.close();
1141
1142 // Withdrawing to self out of the same vault stays unaffected.
1143 env(vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(1)}));
1144 env.close();
1145 }
1146
1147 {
1148 auto const domainId = [&]() {
1149 pdomain::Credentials const credentials{
1150 {.issuer = credIssuer, .credType = credType}};
1151 env(pdomain::setTx(pdOwner, credentials));
1152 env.close();
1153 return pdomain::getNewDomain(env.meta());
1154 }();
1155
1156 env(credentials::create(depositor, credIssuer, credType));
1157 env(credentials::accept(depositor, credIssuer, credType));
1158 env.close();
1159
1160 auto [createTx, keylet] =
1161 vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate});
1162 env(createTx);
1163 env.close();
1164
1165 auto setTx = vault.set({.owner = owner, .id = keylet.key});
1166 setTx[sfDomainID] = to_string(domainId);
1167 env(setTx);
1168 env.close();
1169
1170 env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1'000)}));
1171 env.close();
1172
1173 // The domain check never gets a say: the destination is rejected
1174 // for what it is, not for the domain it is missing.
1175 env(withdrawToPseudo(keylet.key), Ter(expected));
1176 env.close();
1177 }
1178 }
1179
1180public:
1181 void
1194};
1195
1196BEAST_DEFINE_TESTSUITE(VaultValidation, app, xrpl);
1197
1198} // namespace xrpl
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
constexpr bool native() const
Definition Asset.h:125
Integers of any length that is a multiple of 32-bits.
Definition base_uint.h:82
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition SeqProxy.h:62
Shared base for the Vault*_test family under src/test/app/vault/.
FeatureBitset const all_
static constexpr auto kNegativeAmount
test::jtx::PrettyAsset PrettyAsset
void testVaultWithdrawPseudoAccountDestination(FeatureBitset features)
void run() override
Runs the suite.
constexpr Zero kZero
Definition Zero.h:30
Keylet computation functions.
Definition Indexes.h:40
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition Indexes.cpp:232
Keylet vault(AccountID const &owner, SeqProxy const &seq) noexcept
Definition Indexes.cpp:591
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ telINSUF_FEE_P
Definition TER.h:43
@ terNO_RIPPLE
Definition TER.h:225
@ terNO_ACCOUNT
Definition TER.h:218
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:108
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:13
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
constexpr std::size_t kMaxDataPayloadLength
The maximum length of Data payload.
Definition Protocol.h:303
BaseUInt< 256 > UInt256
Definition base_uint.h:580
@ temBAD_FEE
Definition TER.h:80
@ temINVALID_FLAG
Definition TER.h:99
@ temMALFORMED
Definition TER.h:75
@ temDISABLED
Definition TER.h:102
@ temBAD_AMOUNT
Definition TER.h:77
TERSubset< CanCvtToTER > TER
Definition TER.h:654
@ tecWRONG_ASSET
Definition TER.h:368
@ tecPSEUDO_ACCOUNT
Definition TER.h:370
@ tecNO_ENTRY
Definition TER.h:314
@ tecOBJECT_NOT_FOUND
Definition TER.h:334
@ tecNO_AUTH
Definition TER.h:308
@ tecFROZEN
Definition TER.h:311
@ tecINSUFFICIENT_RESERVE
Definition TER.h:315
@ tecNO_PERMISSION
Definition TER.h:313
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
@ tesSUCCESS
Definition TER.h:250
UInt256 key
Definition Keylet.h:21