rippled
Loading...
Searching...
No Matches
Vault_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2024 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 <test/jtx/AMM.h>
21#include <test/jtx/AMMTest.h>
22#include <test/jtx/Account.h>
23#include <test/jtx/Env.h>
24#include <test/jtx/amount.h>
25#include <test/jtx/credentials.h>
26#include <test/jtx/fee.h>
27#include <test/jtx/flags.h>
28#include <test/jtx/mpt.h>
29#include <test/jtx/permissioned_domains.h>
30#include <test/jtx/utility.h>
31#include <test/jtx/vault.h>
32
33#include <xrpld/ledger/View.h>
34
35#include <xrpl/basics/base_uint.h>
36#include <xrpl/beast/unit_test/suite.h>
37#include <xrpl/json/json_forwards.h>
38#include <xrpl/json/json_value.h>
39#include <xrpl/protocol/AccountID.h>
40#include <xrpl/protocol/Asset.h>
41#include <xrpl/protocol/Feature.h>
42#include <xrpl/protocol/Indexes.h>
43#include <xrpl/protocol/Issue.h>
44#include <xrpl/protocol/MPTIssue.h>
45#include <xrpl/protocol/Protocol.h>
46#include <xrpl/protocol/SField.h>
47#include <xrpl/protocol/STAmount.h>
48#include <xrpl/protocol/STNumber.h>
49#include <xrpl/protocol/TER.h>
50#include <xrpl/protocol/TxFlags.h>
51#include <xrpl/protocol/XRPAmount.h>
52#include <xrpl/protocol/jss.h>
53
54namespace ripple {
55
57{
60
61 static auto constexpr negativeAmount =
62 [](PrettyAsset const& asset) -> PrettyAmount {
63 return {STAmount{asset.raw(), 1ul, 0, true, STAmount::unchecked{}}, ""};
64 };
65
66 void
68 {
69 using namespace test::jtx;
70
71 auto const testSequence = [this](
72 std::string const& prefix,
73 Env& env,
74 Account const& issuer,
75 Account const& owner,
76 Account const& depositor,
77 Account const& charlie,
78 Vault& vault,
79 PrettyAsset const& asset) {
80 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
81 tx[sfData] = "AFEED00E";
82 tx[sfAssetsMaximum] = asset(100).number();
83 env(tx);
84 env.close();
85 BEAST_EXPECT(env.le(keylet));
86
87 auto const share = [&env, keylet = keylet, this]() -> PrettyAsset {
88 auto const vault = env.le(keylet);
89 BEAST_EXPECT(vault != nullptr);
90 return MPTIssue(vault->at(sfShareMPTID));
91 }();
92
93 // Several 3rd party accounts which cannot receive funds
94 Account alice{"alice"};
95 Account dave{"dave"};
96 Account erin{"erin"}; // not authorized by issuer
97 env.fund(XRP(1000), alice, dave, erin);
98 env(fset(alice, asfDepositAuth));
99 env(fset(dave, asfRequireDest));
100 env.close();
101
102 {
103 testcase(prefix + " fail to deposit more than assets held");
104 auto tx = vault.deposit(
105 {.depositor = depositor,
106 .id = keylet.key,
107 .amount = asset(10000)});
108 env(tx, ter(tecINSUFFICIENT_FUNDS));
109 }
110
111 {
112 testcase(prefix + " deposit non-zero amount");
113 auto tx = vault.deposit(
114 {.depositor = depositor,
115 .id = keylet.key,
116 .amount = asset(50)});
117 env(tx);
118 }
119
120 {
121 testcase(prefix + " deposit non-zero amount again");
122 auto tx = vault.deposit(
123 {.depositor = depositor,
124 .id = keylet.key,
125 .amount = asset(50)});
126 env(tx);
127 }
128
129 {
130 testcase(prefix + " fail to delete non-empty vault");
131 auto tx = vault.del({.owner = owner, .id = keylet.key});
132 env(tx, ter(tecHAS_OBLIGATIONS));
133 }
134
135 {
136 testcase(prefix + " fail to update because wrong owner");
137 auto tx = vault.set({.owner = issuer, .id = keylet.key});
138 tx[sfAssetsMaximum] = asset(50).number();
139 env(tx, ter(tecNO_PERMISSION));
140 }
141
142 {
143 testcase(
144 prefix + " fail to set maximum lower than current amount");
145 auto tx = vault.set({.owner = owner, .id = keylet.key});
146 tx[sfAssetsMaximum] = asset(50).number();
147 env(tx, ter(tecLIMIT_EXCEEDED));
148 }
149
150 {
151 testcase(prefix + " set maximum higher than current amount");
152 auto tx = vault.set({.owner = owner, .id = keylet.key});
153 tx[sfAssetsMaximum] = asset(150).number();
154 env(tx);
155 }
156
157 {
158 testcase(prefix + " set data");
159 auto tx = vault.set({.owner = owner, .id = keylet.key});
160 tx[sfData] = "0";
161 env(tx);
162 }
163
164 {
165 testcase(prefix + " fail to set domain on public vault");
166 auto tx = vault.set({.owner = owner, .id = keylet.key});
167 tx[sfDomainID] = to_string(base_uint<256>(42ul));
168 env(tx, ter{tecNO_PERMISSION});
169 }
170
171 {
172 testcase(prefix + " fail to deposit more than maximum");
173 auto tx = vault.deposit(
174 {.depositor = depositor,
175 .id = keylet.key,
176 .amount = asset(100)});
177 env(tx, ter(tecLIMIT_EXCEEDED));
178 }
179
180 {
181 testcase(prefix + " reset maximum to zero i.e. not enforced");
182 auto tx = vault.set({.owner = owner, .id = keylet.key});
183 tx[sfAssetsMaximum] = asset(0).number();
184 env(tx);
185 }
186
187 {
188 testcase(prefix + " fail to withdraw more than assets held");
189 auto tx = vault.withdraw(
190 {.depositor = depositor,
191 .id = keylet.key,
192 .amount = asset(1000)});
193 env(tx, ter(tecINSUFFICIENT_FUNDS));
194 }
195
196 {
197 testcase(prefix + " deposit some more");
198 auto tx = vault.deposit(
199 {.depositor = depositor,
200 .id = keylet.key,
201 .amount = asset(100)});
202 env(tx);
203 }
204
205 {
206 testcase(prefix + " clawback some");
207 auto code =
208 asset.raw().native() ? ter(temMALFORMED) : ter(tesSUCCESS);
209 auto tx = vault.clawback(
210 {.issuer = issuer,
211 .id = keylet.key,
212 .holder = depositor,
213 .amount = asset(10)});
214 env(tx, code);
215 }
216
217 {
218 testcase(prefix + " clawback all");
219 auto code = asset.raw().native() ? ter(tecNO_PERMISSION)
220 : ter(tesSUCCESS);
221 auto tx = vault.clawback(
222 {.issuer = issuer, .id = keylet.key, .holder = depositor});
223 env(tx, code);
224 }
225
226 if (!asset.raw().native())
227 {
228 testcase(prefix + " deposit again");
229 auto tx = vault.deposit(
230 {.depositor = depositor,
231 .id = keylet.key,
232 .amount = asset(200)});
233 env(tx);
234 }
235
236 {
237 testcase(
238 prefix + " fail to withdraw to 3rd party lsfDepositAuth");
239 auto tx = vault.withdraw(
240 {.depositor = depositor,
241 .id = keylet.key,
242 .amount = asset(100)});
243 tx[sfDestination] = alice.human();
244 env(tx, ter{tecNO_PERMISSION});
245 }
246
247 if (!asset.raw().native())
248 {
249 testcase(
250 prefix + " fail to withdraw to 3rd party no authorization");
251 auto tx = vault.withdraw(
252 {.depositor = depositor,
253 .id = keylet.key,
254 .amount = asset(100)});
255 tx[sfDestination] = erin.human();
256 env(tx,
257 ter{asset.raw().holds<Issue>() ? tecNO_LINE : tecNO_AUTH});
258 }
259
260 if (!asset.raw().native() && asset.raw().holds<Issue>())
261 {
262 testcase(prefix + " temporary authorization for 3rd party");
263 env(trust(erin, asset(1000)));
264 env(trust(issuer, asset(0), erin, tfSetfAuth));
265 env(pay(issuer, erin, asset(10)));
266
267 // Erin deposits all in vault, then sends shares to depositor
268 auto tx = vault.deposit(
269 {.depositor = erin, .id = keylet.key, .amount = asset(10)});
270 env(tx);
271 env(pay(erin, depositor, share(10)));
272
273 testcase(prefix + " withdraw to authorized 3rd party");
274 // Depositor withdraws shares, destined to Erin
275 tx = vault.withdraw(
276 {.depositor = depositor,
277 .id = keylet.key,
278 .amount = asset(10)});
279 tx[sfDestination] = erin.human();
280 env(tx);
281 // Erin returns assets to issuer
282 env(pay(erin, issuer, asset(10)));
283
284 testcase(prefix + " fail to pay to unauthorized 3rd party");
285 env(trust(erin, asset(0)));
286 // Erin has MPToken but is no longer authorized to hold assets
287 env(pay(depositor, erin, share(1)), ter{tecNO_LINE});
288 }
289
290 {
291 testcase(
292 prefix +
293 " fail to withdraw to 3rd party lsfRequireDestTag");
294 auto tx = vault.withdraw(
295 {.depositor = depositor,
296 .id = keylet.key,
297 .amount = asset(100)});
298 tx[sfDestination] = dave.human();
299 env(tx, ter{tecDST_TAG_NEEDED});
300 }
301
302 {
303 testcase(prefix + " withdraw to authorized 3rd party");
304 auto tx = vault.withdraw(
305 {.depositor = depositor,
306 .id = keylet.key,
307 .amount = asset(100)});
308 tx[sfDestination] = charlie.human();
309 env(tx);
310 }
311
312 {
313 testcase(prefix + " withdraw to issuer");
314 auto tx = vault.withdraw(
315 {.depositor = depositor,
316 .id = keylet.key,
317 .amount = asset(50)});
318 tx[sfDestination] = issuer.human();
319 env(tx);
320 }
321
322 {
323 testcase(prefix + " withdraw remaining assets");
324 auto tx = vault.withdraw(
325 {.depositor = depositor,
326 .id = keylet.key,
327 .amount = asset(50)});
328 env(tx);
329 }
330
331 {
332 testcase(prefix + " fail to delete because wrong owner");
333 auto tx = vault.del({.owner = issuer, .id = keylet.key});
334 env(tx, ter(tecNO_PERMISSION));
335 }
336
337 {
338 testcase(prefix + " delete empty vault");
339 auto tx = vault.del({.owner = owner, .id = keylet.key});
340 env(tx);
341 BEAST_EXPECT(!env.le(keylet));
342 }
343 };
344
345 auto testCases = [this, &testSequence](
346 std::string prefix,
348 Env & env,
349 Account const& issuer,
350 Account const& owner,
351 Account const& depositor,
352 Account const& charlie)> setup) {
353 Env env{*this, supported_amendments() | featureSingleAssetVault};
354 Account issuer{"issuer"};
355 Account owner{"owner"};
356 Account depositor{"depositor"};
357 Account charlie{"charlie"}; // authorized 3rd party
358 Vault vault{env};
359 env.fund(XRP(1000), issuer, owner, depositor, charlie);
360 env.close();
361 env(fset(issuer, asfAllowTrustLineClawback));
362 env(fset(issuer, asfRequireAuth));
363 env.close();
364 env.require(flags(issuer, asfAllowTrustLineClawback));
365 env.require(flags(issuer, asfRequireAuth));
366
367 PrettyAsset asset = setup(env, issuer, owner, depositor, charlie);
368 testSequence(
369 prefix, env, issuer, owner, depositor, charlie, vault, asset);
370 };
371
372 testCases(
373 "XRP",
374 [](Env& env,
375 Account const& issuer,
376 Account const& owner,
377 Account const& depositor,
378 Account const& charlie) -> PrettyAsset {
379 return {xrpIssue(), 1'000'000};
380 });
381
382 testCases(
383 "IOU",
384 [](Env& env,
385 Account const& issuer,
386 Account const& owner,
387 Account const& depositor,
388 Account const& charlie) -> Asset {
389 PrettyAsset asset = issuer["IOU"];
390 env(trust(owner, asset(1000)));
391 env(trust(depositor, asset(1000)));
392 env(trust(charlie, asset(1000)));
393 env(trust(issuer, asset(0), owner, tfSetfAuth));
394 env(trust(issuer, asset(0), depositor, tfSetfAuth));
395 env(trust(issuer, asset(0), charlie, tfSetfAuth));
396 env(pay(issuer, depositor, asset(1000)));
397 env.close();
398 return asset;
399 });
400
401 testCases(
402 "MPT",
403 [](Env& env,
404 Account const& issuer,
405 Account const& owner,
406 Account const& depositor,
407 Account const& charlie) -> Asset {
408 MPTTester mptt{env, issuer, mptInitNoFund};
409 mptt.create(
410 {.flags =
412 PrettyAsset asset = mptt.issuanceID();
413 mptt.authorize({.account = depositor});
414 mptt.authorize({.account = charlie});
415 env(pay(issuer, depositor, asset(1000)));
416 env.close();
417 return asset;
418 });
419 }
420
421 void
423 {
424 using namespace test::jtx;
425
426 struct CaseArgs
427 {
428 FeatureBitset features =
429 supported_amendments() | featureSingleAssetVault;
430 };
431
432 auto testCase = [&, this](
433 std::function<void(
434 Env & env,
435 Account const& issuer,
436 Account const& owner,
437 Asset const& asset,
438 Vault& vault)> test,
439 CaseArgs args = {}) {
440 Env env{*this, args.features};
441 Account issuer{"issuer"};
442 Account owner{"owner"};
443 Vault vault{env};
444 env.fund(XRP(1000), issuer, owner);
445 env.close();
446
447 env(fset(issuer, asfAllowTrustLineClawback));
448 env(fset(issuer, asfRequireAuth));
449 env.close();
450
451 PrettyAsset asset = issuer["IOU"];
452 env(trust(owner, asset(1000)));
453 env(trust(issuer, asset(0), owner, tfSetfAuth));
454 env(pay(issuer, owner, asset(1000)));
455 env.close();
456
457 test(env, issuer, owner, asset, vault);
458 };
459
460 testCase(
461 [&](Env& env,
462 Account const& issuer,
463 Account const& owner,
464 Asset const& asset,
465 Vault& vault) {
466 testcase("disabled single asset vault");
467
468 auto [tx, keylet] =
469 vault.create({.owner = owner, .asset = asset});
470 env(tx, ter{temDISABLED});
471
472 {
473 auto tx = vault.set({.owner = owner, .id = keylet.key});
474 env(tx, ter{temDISABLED});
475 }
476
477 {
478 auto tx = vault.deposit(
479 {.depositor = owner,
480 .id = keylet.key,
481 .amount = asset(10)});
482 env(tx, ter{temDISABLED});
483 }
484
485 {
486 auto tx = vault.withdraw(
487 {.depositor = owner,
488 .id = keylet.key,
489 .amount = asset(10)});
490 env(tx, ter{temDISABLED});
491 }
492
493 {
494 auto tx = vault.clawback(
495 {.issuer = issuer,
496 .id = keylet.key,
497 .holder = owner,
498 .amount = asset(10)});
499 env(tx, ter{temDISABLED});
500 }
501
502 {
503 auto tx = vault.del({.owner = owner, .id = keylet.key});
504 env(tx, ter{temDISABLED});
505 }
506 },
507 {.features = supported_amendments() - featureSingleAssetVault});
508
509 testCase([&](Env& env,
510 Account const& issuer,
511 Account const& owner,
512 Asset const& asset,
513 Vault& vault) {
514 testcase("invalid flags");
515
516 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
517 tx[sfFlags] = tfClearDeepFreeze;
518 env(tx, ter{temINVALID_FLAG});
519
520 {
521 auto tx = vault.set({.owner = owner, .id = keylet.key});
522 tx[sfFlags] = tfClearDeepFreeze;
523 env(tx, ter{temINVALID_FLAG});
524 }
525
526 {
527 auto tx = vault.deposit(
528 {.depositor = owner,
529 .id = keylet.key,
530 .amount = asset(10)});
531 tx[sfFlags] = tfClearDeepFreeze;
532 env(tx, ter{temINVALID_FLAG});
533 }
534
535 {
536 auto tx = vault.withdraw(
537 {.depositor = owner,
538 .id = keylet.key,
539 .amount = asset(10)});
540 tx[sfFlags] = tfClearDeepFreeze;
541 env(tx, ter{temINVALID_FLAG});
542 }
543
544 {
545 auto tx = vault.clawback(
546 {.issuer = issuer,
547 .id = keylet.key,
548 .holder = owner,
549 .amount = asset(10)});
550 tx[sfFlags] = tfClearDeepFreeze;
551 env(tx, ter{temINVALID_FLAG});
552 }
553
554 {
555 auto tx = vault.del({.owner = owner, .id = keylet.key});
556 tx[sfFlags] = tfClearDeepFreeze;
557 env(tx, ter{temINVALID_FLAG});
558 }
559 });
560
561 testCase([&](Env& env,
562 Account const& issuer,
563 Account const& owner,
564 Asset const& asset,
565 Vault& vault) {
566 testcase("invalid fee");
567
568 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
569 tx[jss::Fee] = "-1";
570 env(tx, ter{temBAD_FEE});
571
572 {
573 auto tx = vault.set({.owner = owner, .id = keylet.key});
574 tx[jss::Fee] = "-1";
575 env(tx, ter{temBAD_FEE});
576 }
577
578 {
579 auto tx = vault.deposit(
580 {.depositor = owner,
581 .id = keylet.key,
582 .amount = asset(10)});
583 tx[jss::Fee] = "-1";
584 env(tx, ter{temBAD_FEE});
585 }
586
587 {
588 auto tx = vault.withdraw(
589 {.depositor = owner,
590 .id = keylet.key,
591 .amount = asset(10)});
592 tx[jss::Fee] = "-1";
593 env(tx, ter{temBAD_FEE});
594 }
595
596 {
597 auto tx = vault.clawback(
598 {.issuer = issuer,
599 .id = keylet.key,
600 .holder = owner,
601 .amount = asset(10)});
602 tx[jss::Fee] = "-1";
603 env(tx, ter{temBAD_FEE});
604 }
605
606 {
607 auto tx = vault.del({.owner = owner, .id = keylet.key});
608 tx[jss::Fee] = "-1";
609 env(tx, ter{temBAD_FEE});
610 }
611 });
612
613 testCase(
614 [&](Env& env,
615 Account const&,
616 Account const& owner,
617 Asset const&,
618 Vault& vault) {
619 testcase("disabled permissioned domain");
620
621 auto [tx, keylet] =
622 vault.create({.owner = owner, .asset = xrpIssue()});
623 tx[sfDomainID] = to_string(base_uint<256>(42ul));
624 env(tx, ter{temDISABLED});
625
626 {
627 auto tx = vault.set({.owner = owner, .id = keylet.key});
628 tx[sfDomainID] = to_string(base_uint<256>(42ul));
629 env(tx, ter{temDISABLED});
630 }
631
632 {
633 auto tx = vault.set({.owner = owner, .id = keylet.key});
634 tx[sfDomainID] = "0";
635 env(tx, ter{temDISABLED});
636 }
637 },
638 {.features = (supported_amendments() | featureSingleAssetVault) -
639 featurePermissionedDomains});
640
641 testCase([&](Env& env,
642 Account const& issuer,
643 Account const& owner,
644 Asset const& asset,
645 Vault& vault) {
646 testcase("use zero vault");
647
648 auto [tx, keylet] =
649 vault.create({.owner = owner, .asset = xrpIssue()});
650
651 {
652 auto tx = vault.set({
653 .owner = owner,
654 .id = beast::zero,
655 });
656 env(tx, ter{temMALFORMED});
657 }
658
659 {
660 auto tx = vault.deposit(
661 {.depositor = owner,
662 .id = beast::zero,
663 .amount = asset(10)});
664 env(tx, ter(temMALFORMED));
665 }
666
667 {
668 auto tx = vault.withdraw(
669 {.depositor = owner,
670 .id = beast::zero,
671 .amount = asset(10)});
672 env(tx, ter{temMALFORMED});
673 }
674
675 {
676 auto tx = vault.clawback(
677 {.issuer = issuer,
678 .id = beast::zero,
679 .holder = owner,
680 .amount = asset(10)});
681 env(tx, ter{temMALFORMED});
682 }
683
684 {
685 auto tx = vault.del({
686 .owner = owner,
687 .id = beast::zero,
688 });
689 env(tx, ter{temMALFORMED});
690 }
691 });
692
693 testCase([&](Env& env,
694 Account const& issuer,
695 Account const& owner,
696 Asset const& asset,
697 Vault& vault) {
698 testcase("clawback from self");
699
700 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
701
702 {
703 auto tx = vault.clawback(
704 {.issuer = issuer,
705 .id = keylet.key,
706 .holder = issuer,
707 .amount = asset(10)});
708 env(tx, ter{temMALFORMED});
709 }
710 });
711
712 testCase([&](Env& env,
713 Account const&,
714 Account const& owner,
715 Asset const& asset,
716 Vault& vault) {
717 testcase("withdraw to bad destination");
718
719 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
720
721 {
722 auto tx = vault.withdraw(
723 {.depositor = owner,
724 .id = keylet.key,
725 .amount = asset(10)});
726 tx[jss::Destination] = "0";
727 env(tx, ter{temMALFORMED});
728 }
729 });
730
731 testCase([&](Env& env,
732 Account const&,
733 Account const& owner,
734 Asset const& asset,
735 Vault& vault) {
736 testcase("create or set invalid data");
737
738 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
739
740 {
741 auto tx = tx1;
742 tx[sfData] = "";
743 env(tx, ter(temMALFORMED));
744 }
745
746 {
747 auto tx = tx1;
748 // A hexadecimal string of 257 bytes.
749 tx[sfData] = std::string(514, 'A');
750 env(tx, ter(temMALFORMED));
751 }
752
753 {
754 auto tx = vault.set({.owner = owner, .id = keylet.key});
755 tx[sfData] = "";
756 env(tx, ter{temMALFORMED});
757 }
758
759 {
760 auto tx = vault.set({.owner = owner, .id = keylet.key});
761 // A hexadecimal string of 257 bytes.
762 tx[sfData] = std::string(514, 'A');
763 env(tx, ter{temMALFORMED});
764 }
765 });
766
767 testCase([&](Env& env,
768 Account const&,
769 Account const& owner,
770 Asset const& asset,
771 Vault& vault) {
772 testcase("set nothing updated");
773
774 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
775
776 {
777 auto tx = vault.set({.owner = owner, .id = keylet.key});
778 env(tx, ter{temMALFORMED});
779 }
780 });
781
782 testCase([&](Env& env,
783 Account const&,
784 Account const& owner,
785 Asset const& asset,
786 Vault& vault) {
787 testcase("create with invalid metadata");
788
789 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
790
791 {
792 auto tx = tx1;
793 tx[sfMPTokenMetadata] = "";
794 env(tx, ter(temMALFORMED));
795 }
796
797 {
798 auto tx = tx1;
799 // This metadata is for the share token.
800 // A hexadecimal string of 1025 bytes.
801 tx[sfMPTokenMetadata] = std::string(2050, 'B');
802 env(tx, ter(temMALFORMED));
803 }
804 });
805
806 testCase([&](Env& env,
807 Account const&,
808 Account const& owner,
809 Asset const& asset,
810 Vault& vault) {
811 testcase("set negative maximum");
812
813 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
814
815 {
816 auto tx = vault.set({.owner = owner, .id = keylet.key});
817 tx[sfAssetsMaximum] = negativeAmount(asset).number();
818 env(tx, ter{temMALFORMED});
819 }
820 });
821
822 testCase([&](Env& env,
823 Account const&,
824 Account const& owner,
825 Asset const& asset,
826 Vault& vault) {
827 testcase("invalid deposit amount");
828
829 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
830
831 {
832 auto tx = vault.deposit(
833 {.depositor = owner,
834 .id = keylet.key,
835 .amount = negativeAmount(asset)});
836 env(tx, ter(temBAD_AMOUNT));
837 }
838
839 {
840 auto tx = vault.deposit(
841 {.depositor = owner, .id = keylet.key, .amount = asset(0)});
842 env(tx, ter(temBAD_AMOUNT));
843 }
844 });
845
846 testCase([&](Env& env,
847 Account const&,
848 Account const& owner,
849 Asset const& asset,
850 Vault& vault) {
851 testcase("invalid set immutable flag");
852
853 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
854
855 {
856 auto tx = vault.set({.owner = owner, .id = keylet.key});
857 tx[sfFlags] = tfVaultPrivate;
858 env(tx, ter(temINVALID_FLAG));
859 }
860 });
861
862 testCase([&](Env& env,
863 Account const&,
864 Account const& owner,
865 Asset const& asset,
866 Vault& vault) {
867 testcase("invalid withdraw amount");
868
869 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
870
871 {
872 auto tx = vault.withdraw(
873 {.depositor = owner,
874 .id = keylet.key,
875 .amount = negativeAmount(asset)});
876 env(tx, ter(temBAD_AMOUNT));
877 }
878
879 {
880 auto tx = vault.withdraw(
881 {.depositor = owner, .id = keylet.key, .amount = asset(0)});
882 env(tx, ter(temBAD_AMOUNT));
883 }
884 });
885
886 testCase([&](Env& env,
887 Account const& issuer,
888 Account const& owner,
889 Asset const& asset,
890 Vault& vault) {
891 testcase("invalid clawback");
892
893 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
894
895 {
896 auto tx = vault.clawback(
897 {.issuer = owner,
898 .id = keylet.key,
899 .holder = issuer,
900 .amount = asset(50)});
901 env(tx, ter(temMALFORMED));
902 }
903
904 {
905 auto tx = vault.clawback(
906 {.issuer = issuer,
907 .id = keylet.key,
908 .holder = owner,
909 .amount = negativeAmount(asset)});
910 env(tx, ter(temBAD_AMOUNT));
911 }
912 });
913
914 testCase([&](Env& env,
915 Account const&,
916 Account const& owner,
917 Asset const& asset,
918 Vault& vault) {
919 testcase("invalid create");
920
921 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
922
923 {
924 auto tx = tx1;
925 tx[sfWithdrawalPolicy] = 0;
926 env(tx, ter(temMALFORMED));
927 }
928
929 {
930 auto tx = tx1;
931 tx[sfDomainID] = to_string(base_uint<256>(42ul));
932 env(tx, ter{temMALFORMED});
933 }
934
935 {
936 auto tx = tx1;
937 tx[sfAssetsMaximum] = negativeAmount(asset).number();
938 env(tx, ter{temMALFORMED});
939 }
940
941 {
942 auto tx = tx1;
943 tx[sfFlags] = tfVaultPrivate;
944 tx[sfDomainID] = "0";
945 env(tx, ter{temMALFORMED});
946 }
947 });
948 }
949
950 // Test for non-asset specific behaviors.
951 void
953 {
954 using namespace test::jtx;
955
956 auto testCase = [this](std::function<void(
957 Env & env,
958 Account const& issuer,
959 Account const& owner,
960 Account const& depositor,
961 Asset const& asset,
962 Vault& vault)> test) {
963 Env env{*this, supported_amendments() | featureSingleAssetVault};
964 Account issuer{"issuer"};
965 Account owner{"owner"};
966 Account depositor{"depositor"};
967 env.fund(XRP(1000), issuer, owner, depositor);
968 env.close();
969 Vault vault{env};
970 Asset asset = xrpIssue();
971
972 test(env, issuer, owner, depositor, asset, vault);
973 };
974
975 testCase([this](
976 Env& env,
977 Account const& issuer,
978 Account const& owner,
979 Account const& depositor,
980 PrettyAsset const& asset,
981 Vault& vault) {
982 testcase("nothing to set");
983 auto tx = vault.set({.owner = owner, .id = keylet::skip().key});
984 tx[sfAssetsMaximum] = asset(0).number();
985 env(tx, ter(tecNO_ENTRY));
986 });
987
988 testCase([this](
989 Env& env,
990 Account const& issuer,
991 Account const& owner,
992 Account const& depositor,
993 PrettyAsset const& asset,
994 Vault& vault) {
995 testcase("nothing to deposit to");
996 auto tx = vault.deposit(
997 {.depositor = depositor,
998 .id = keylet::skip().key,
999 .amount = asset(10)});
1000 env(tx, ter(tecNO_ENTRY));
1001 });
1002
1003 testCase([this](
1004 Env& env,
1005 Account const& issuer,
1006 Account const& owner,
1007 Account const& depositor,
1008 PrettyAsset const& asset,
1009 Vault& vault) {
1010 testcase("nothing to withdraw from");
1011 auto tx = vault.withdraw(
1012 {.depositor = depositor,
1013 .id = keylet::skip().key,
1014 .amount = asset(10)});
1015 env(tx, ter(tecNO_ENTRY));
1016 });
1017
1018 testCase([this](
1019 Env& env,
1020 Account const& issuer,
1021 Account const& owner,
1022 Account const& depositor,
1023 Asset const& asset,
1024 Vault& vault) {
1025 testcase("nothing to delete");
1026 auto tx = vault.del({.owner = owner, .id = keylet::skip().key});
1027 env(tx, ter(tecNO_ENTRY));
1028 });
1029
1030 testCase([this](
1031 Env& env,
1032 Account const& issuer,
1033 Account const& owner,
1034 Account const& depositor,
1035 Asset const& asset,
1036 Vault& vault) {
1037 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1038 testcase("transaction is good");
1039 env(tx);
1040 });
1041
1042 testCase([this](
1043 Env& env,
1044 Account const& issuer,
1045 Account const& owner,
1046 Account const& depositor,
1047 Asset const& asset,
1048 Vault& vault) {
1049 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1050 tx[sfWithdrawalPolicy] = 1;
1051 testcase("explicitly select withdrawal policy");
1052 env(tx);
1053 });
1054
1055 testCase([this](
1056 Env& env,
1057 Account const& issuer,
1058 Account const& owner,
1059 Account const& depositor,
1060 Asset const& asset,
1061 Vault& vault) {
1062 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1063 testcase("insufficient fee");
1064 env(tx, fee(env.current()->fees().base), ter(telINSUF_FEE_P));
1065 });
1066
1067 testCase([this](
1068 Env& env,
1069 Account const& issuer,
1070 Account const& owner,
1071 Account const& depositor,
1072 Asset const& asset,
1073 Vault& vault) {
1074 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1075 testcase("insufficient reserve");
1076 // It is possible to construct a complicated mathematical
1077 // expression for this amount, but it is sadly not easy.
1078 env(pay(owner, issuer, XRP(775)));
1079 env.close();
1080 env(tx, ter(tecINSUFFICIENT_RESERVE));
1081 });
1082
1083 testCase([this](
1084 Env& env,
1085 Account const& issuer,
1086 Account const& owner,
1087 Account const& depositor,
1088 Asset const& asset,
1089 Vault& vault) {
1090 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1091 tx[sfFlags] = tfVaultPrivate;
1092 tx[sfDomainID] = to_string(base_uint<256>(42ul));
1093 testcase("non-existing domain");
1094 env(tx, ter{tecOBJECT_NOT_FOUND});
1095 });
1096 }
1097
1098 void
1100 {
1101 using namespace test::jtx;
1102 {
1103 {
1104 testcase("IOU fail create frozen");
1105 Env env{
1106 *this, supported_amendments() | featureSingleAssetVault};
1107 Account issuer{"issuer"};
1108 Account owner{"owner"};
1109 env.fund(XRP(1000), issuer, owner);
1110 env.close();
1111 env(fset(issuer, asfGlobalFreeze));
1112 env.close();
1113
1114 Vault vault{env};
1115 Asset asset = issuer["IOU"];
1116 auto [tx, keylet] =
1117 vault.create({.owner = owner, .asset = asset});
1118
1119 env(tx, ter(tecFROZEN));
1120 env.close();
1121 }
1122
1123 {
1124 testcase("IOU fail create no ripling");
1125 Env env{
1126 *this, supported_amendments() | featureSingleAssetVault};
1127 Account issuer{"issuer"};
1128 Account owner{"owner"};
1129 env.fund(XRP(1000), issuer, owner);
1130 env.close();
1131 env(fclear(issuer, asfDefaultRipple));
1132 env.close();
1133
1134 Vault vault{env};
1135 Asset asset = issuer["IOU"];
1136 auto [tx, keylet] =
1137 vault.create({.owner = owner, .asset = asset});
1138 env(tx, ter(terNO_RIPPLE));
1139 env.close();
1140 }
1141
1142 {
1143 testcase("IOU no issuer");
1144 Env env{
1145 *this, supported_amendments() | featureSingleAssetVault};
1146 Account issuer{"issuer"};
1147 Account owner{"owner"};
1148 env.fund(XRP(1000), owner);
1149 env.close();
1150
1151 Vault vault{env};
1152 Asset asset = issuer["IOU"];
1153 {
1154 auto [tx, keylet] =
1155 vault.create({.owner = owner, .asset = asset});
1156 env(tx, ter(terNO_ACCOUNT));
1157 env.close();
1158 }
1159 }
1160 }
1161
1162 {
1163 testcase("IOU fail create vault for AMM LPToken");
1164 Env env{*this, supported_amendments() | featureSingleAssetVault};
1165 Account const gw("gateway");
1166 Account const alice("alice");
1167 Account const carol("carol");
1168 IOU const USD = gw["USD"];
1169
1170 auto const [asset1, asset2] =
1171 std::pair<STAmount, STAmount>(XRP(10000), USD(10000));
1172 auto tofund = [&](STAmount const& a) -> STAmount {
1173 if (a.native())
1174 {
1175 auto const defXRP = XRP(30000);
1176 if (a <= defXRP)
1177 return defXRP;
1178 return a + XRP(1000);
1179 }
1180 auto const defIOU = STAmount{a.issue(), 30000};
1181 if (a <= defIOU)
1182 return defIOU;
1183 return a + STAmount{a.issue(), 1000};
1184 };
1185 auto const toFund1 = tofund(asset1);
1186 auto const toFund2 = tofund(asset2);
1187 BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2);
1188
1189 if (!asset1.native() && !asset2.native())
1190 fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All);
1191 else if (asset1.native())
1192 fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All);
1193 else if (asset2.native())
1194 fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All);
1195
1196 AMM ammAlice(
1197 env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0});
1198
1199 Account const owner{"owner"};
1200 env.fund(XRP(1000000), owner);
1201
1202 Vault vault{env};
1203 auto [tx, k] =
1204 vault.create({.owner = owner, .asset = ammAlice.lptIssue()});
1205 env(tx, ter{tecWRONG_ASSET});
1206 env.close();
1207 }
1208 }
1209
1210 void
1212 {
1213 using namespace test::jtx;
1214
1215 Env env{*this, supported_amendments() | featureSingleAssetVault};
1216 Account issuer{"issuer"};
1217 Account owner{"owner"};
1218 Account depositor{"depositor"};
1219 env.fund(XRP(1000), issuer, owner, depositor);
1220 env.close();
1221 Vault vault{env};
1222
1223 MPTTester mptt{env, issuer, mptInitNoFund};
1224
1225 // Locked because that is the default flag.
1226 mptt.create();
1227 Asset asset = mptt.issuanceID();
1228 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1229 env(tx, ter(tecNO_AUTH));
1230 }
1231
1232 void
1234 {
1235 using namespace test::jtx;
1236
1237 Env env{*this, supported_amendments() | featureSingleAssetVault};
1238 Account issuer{"issuer"};
1239 Account owner{"owner"};
1240 Account depositor{"depositor"};
1241 env.fund(XRP(1000), issuer, owner, depositor);
1242 env.close();
1243
1244 Vault vault{env};
1245 PrettyAsset asset = issuer["IOU"];
1246 env.trust(asset(1000), owner);
1247 env(pay(issuer, owner, asset(100)));
1248 env.trust(asset(1000), depositor);
1249 env(pay(issuer, depositor, asset(100)));
1250 env.close();
1251
1252 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1253 tx[sfFlags] = tfVaultShareNonTransferable;
1254 env(tx);
1255 env.close();
1256
1257 {
1258 testcase("nontransferable deposits");
1259 auto tx1 = vault.deposit(
1260 {.depositor = depositor,
1261 .id = keylet.key,
1262 .amount = asset(40)});
1263 env(tx1);
1264
1265 auto tx2 = vault.deposit(
1266 {.depositor = owner, .id = keylet.key, .amount = asset(60)});
1267 env(tx2);
1268 env.close();
1269 }
1270
1271 auto const vaultAccount = //
1272 [&env, key = keylet.key, this]() -> AccountID {
1273 auto jvVault = env.rpc("vault_info", strHex(key));
1274
1275 BEAST_EXPECT(
1276 jvVault[jss::result][jss::vault][sfAssetsTotal] == "100");
1277 BEAST_EXPECT(
1278 jvVault[jss::result][jss::vault][jss::shares]
1279 [sfOutstandingAmount] == "100");
1280
1281 // Vault pseudo-account
1282 return parseBase58<AccountID>(
1283 jvVault[jss::result][jss::vault][jss::Account]
1284 .asString())
1285 .value();
1286 }();
1287
1288 auto const MptID = makeMptID(1, vaultAccount);
1289 Asset shares = MptID;
1290
1291 {
1292 testcase("nontransferable shares cannot be moved");
1293 env(pay(owner, depositor, shares(10)), ter{tecNO_AUTH});
1294 env(pay(depositor, owner, shares(10)), ter{tecNO_AUTH});
1295 }
1296
1297 {
1298 testcase("nontransferable shares can be used to withdraw");
1299 auto tx1 = vault.withdraw(
1300 {.depositor = depositor,
1301 .id = keylet.key,
1302 .amount = asset(20)});
1303 env(tx1);
1304
1305 auto tx2 = vault.withdraw(
1306 {.depositor = owner, .id = keylet.key, .amount = asset(30)});
1307 env(tx2);
1308 env.close();
1309 }
1310
1311 {
1312 testcase("nontransferable shares balance check");
1313 auto jvVault = env.rpc("vault_info", strHex(keylet.key));
1314 BEAST_EXPECT(
1315 jvVault[jss::result][jss::vault][sfAssetsTotal] == "50");
1316 BEAST_EXPECT(
1317 jvVault[jss::result][jss::vault][jss::shares]
1318 [sfOutstandingAmount] == "50");
1319 }
1320
1321 {
1322 testcase("nontransferable shares withdraw rest");
1323 auto tx1 = vault.withdraw(
1324 {.depositor = depositor,
1325 .id = keylet.key,
1326 .amount = asset(20)});
1327 env(tx1);
1328
1329 auto tx2 = vault.withdraw(
1330 {.depositor = owner, .id = keylet.key, .amount = asset(30)});
1331 env(tx2);
1332 env.close();
1333 }
1334
1335 {
1336 testcase("nontransferable shares delete empty vault");
1337 auto tx = vault.del({.owner = owner, .id = keylet.key});
1338 env(tx);
1339 BEAST_EXPECT(!env.le(keylet));
1340 }
1341 }
1342
1343 void
1345 {
1346 using namespace test::jtx;
1347
1348 struct CaseArgs
1349 {
1350 bool enableClawback = true;
1351 };
1352
1353 auto testCase = [this](
1354 std::function<void(
1355 Env & env,
1356 Account const& issuer,
1357 Account const& owner,
1358 Account const& depositor,
1359 Asset const& asset,
1360 Vault& vault,
1361 MPTTester& mptt)> test,
1362 CaseArgs args = {}) {
1363 Env env{*this, supported_amendments() | featureSingleAssetVault};
1364 Account issuer{"issuer"};
1365 Account owner{"owner"};
1366 Account depositor{"depositor"};
1367 env.fund(XRP(1000), issuer, owner, depositor);
1368 env.close();
1369 Vault vault{env};
1370
1371 MPTTester mptt{env, issuer, mptInitNoFund};
1372 mptt.create(
1373 {.flags = tfMPTCanTransfer | tfMPTCanLock |
1374 (args.enableClawback ? lsfMPTCanClawback
1375 : LedgerSpecificFlags(0)) |
1377 PrettyAsset asset = mptt.issuanceID();
1378 mptt.authorize({.account = owner});
1379 mptt.authorize({.account = issuer, .holder = owner});
1380 mptt.authorize({.account = depositor});
1381 mptt.authorize({.account = issuer, .holder = depositor});
1382 env(pay(issuer, depositor, asset(1000)));
1383 env.close();
1384
1385 test(env, issuer, owner, depositor, asset, vault, mptt);
1386 };
1387
1388 testCase([this](
1389 Env& env,
1390 Account const& issuer,
1391 Account const& owner,
1392 Account const& depositor,
1393 PrettyAsset const& asset,
1394 Vault& vault,
1395 MPTTester& mptt) {
1396 testcase("MPT nothing to clawback from");
1397 auto tx = vault.clawback(
1398 {.issuer = issuer,
1399 .id = keylet::skip().key,
1400 .holder = depositor,
1401 .amount = asset(10)});
1402 env(tx, ter(tecNO_ENTRY));
1403 });
1404
1405 testCase([this](
1406 Env& env,
1407 Account const& issuer,
1408 Account const& owner,
1409 Account const& depositor,
1410 Asset const& asset,
1411 Vault& vault,
1412 MPTTester& mptt) {
1413 testcase("MPT global lock blocks create");
1414 mptt.set({.account = issuer, .flags = tfMPTLock});
1415 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1416 env(tx, ter(tecLOCKED));
1417 });
1418
1419 testCase([this](
1420 Env& env,
1421 Account const& issuer,
1422 Account const& owner,
1423 Account const& depositor,
1424 Asset const& asset,
1425 Vault& vault,
1426 MPTTester& mptt) {
1427 testcase("MPT global lock blocks deposit");
1428 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1429 env(tx);
1430 env.close();
1431
1432 mptt.set({.account = issuer, .flags = tfMPTLock});
1433 env.close();
1434
1435 tx = vault.deposit(
1436 {.depositor = depositor,
1437 .id = keylet.key,
1438 .amount = asset(100)});
1439 env(tx, ter{tecLOCKED});
1440 env.close();
1441
1442 // Can delete empty vault, even if global lock
1443 tx = vault.del({.owner = owner, .id = keylet.key});
1444 env(tx);
1445 });
1446
1447 testCase([this](
1448 Env& env,
1449 Account const& issuer,
1450 Account const& owner,
1451 Account const& depositor,
1452 Asset const& asset,
1453 Vault& vault,
1454 MPTTester& mptt) {
1455 testcase("MPT global lock blocks withdrawal");
1456 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1457 env(tx);
1458 env.close();
1459 tx = vault.deposit(
1460 {.depositor = depositor,
1461 .id = keylet.key,
1462 .amount = asset(100)});
1463 env(tx);
1464 env.close();
1465
1466 // Check that the OutstandingAmount field of MPTIssuance
1467 // accounts for the issued shares.
1468 auto v = env.le(keylet);
1469 BEAST_EXPECT(v);
1470 MPTID share = (*v)[sfShareMPTID];
1471 auto issuance = env.le(keylet::mptIssuance(share));
1472 BEAST_EXPECT(issuance);
1473 Number outstandingShares = issuance->at(sfOutstandingAmount);
1474 BEAST_EXPECT(outstandingShares == 100);
1475
1476 mptt.set({.account = issuer, .flags = tfMPTLock});
1477 env.close();
1478
1479 tx = vault.withdraw(
1480 {.depositor = depositor,
1481 .id = keylet.key,
1482 .amount = asset(100)});
1483 env(tx, ter(tecLOCKED));
1484
1485 tx[sfDestination] = issuer.human();
1486 env(tx, ter(tecLOCKED));
1487
1488 // Clawback is still permitted, even with global lock
1489 tx = vault.clawback(
1490 {.issuer = issuer,
1491 .id = keylet.key,
1492 .holder = depositor,
1493 .amount = asset(0)});
1494 env(tx);
1495 env.close();
1496
1497 // Can delete empty vault, even if global lock
1498 tx = vault.del({.owner = owner, .id = keylet.key});
1499 env(tx);
1500 });
1501
1502 testCase([this](
1503 Env& env,
1504 Account const& issuer,
1505 Account const& owner,
1506 Account const& depositor,
1507 PrettyAsset const& asset,
1508 Vault& vault,
1509 MPTTester& mptt) {
1510 testcase("MPT only issuer can clawback");
1511
1512 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1513 env(tx);
1514 env.close();
1515
1516 tx = vault.deposit(
1517 {.depositor = depositor,
1518 .id = keylet.key,
1519 .amount = asset(100)});
1520 env(tx);
1521 env.close();
1522
1523 {
1524 auto tx = vault.clawback(
1525 {.issuer = owner, .id = keylet.key, .holder = depositor});
1526 env(tx, ter(tecNO_PERMISSION));
1527 }
1528 });
1529
1530 testCase([this](
1531 Env& env,
1532 Account const& issuer,
1533 Account const& owner,
1534 Account const& depositor,
1535 PrettyAsset const& asset,
1536 Vault& vault,
1537 MPTTester& mptt) {
1538 testcase("MPT issuance deleted");
1539
1540 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1541 env(tx);
1542 env.close();
1543
1544 tx = vault.deposit(
1545 {.depositor = depositor,
1546 .id = keylet.key,
1547 .amount = asset(1000)});
1548 env(tx);
1549 env.close();
1550
1551 {
1552 auto tx = vault.clawback(
1553 {.issuer = issuer,
1554 .id = keylet.key,
1555 .holder = depositor,
1556 .amount = asset(0)});
1557 env(tx);
1558 }
1559
1560 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
1561 env.close();
1562
1563 {
1564 auto [tx, keylet] =
1565 vault.create({.owner = depositor, .asset = asset});
1566 env(tx, ter{tecOBJECT_NOT_FOUND});
1567 }
1568
1569 {
1570 auto tx = vault.deposit(
1571 {.depositor = depositor,
1572 .id = keylet.key,
1573 .amount = asset(10)});
1574 env(tx, ter{tecOBJECT_NOT_FOUND});
1575 }
1576
1577 {
1578 auto tx = vault.withdraw(
1579 {.depositor = depositor,
1580 .id = keylet.key,
1581 .amount = asset(10)});
1582 env(tx, ter{tecOBJECT_NOT_FOUND});
1583 }
1584
1585 {
1586 auto tx = vault.clawback(
1587 {.issuer = issuer,
1588 .id = keylet.key,
1589 .holder = depositor,
1590 .amount = asset(0)});
1591 env(tx, ter{tecOBJECT_NOT_FOUND});
1592 }
1593
1594 env(vault.del({.owner = owner, .id = keylet.key}));
1595 });
1596
1597 testCase(
1598 [this](
1599 Env& env,
1600 Account const& issuer,
1601 Account const& owner,
1602 Account const& depositor,
1603 PrettyAsset const& asset,
1604 Vault& vault,
1605 MPTTester& mptt) {
1606 testcase("MPT clawback disabled");
1607
1608 auto [tx, keylet] =
1609 vault.create({.owner = owner, .asset = asset});
1610 env(tx);
1611 env.close();
1612
1613 tx = vault.deposit(
1614 {.depositor = depositor,
1615 .id = keylet.key,
1616 .amount = asset(1000)});
1617 env(tx);
1618 env.close();
1619
1620 {
1621 auto tx = vault.clawback(
1622 {.issuer = issuer,
1623 .id = keylet.key,
1624 .holder = depositor,
1625 .amount = asset(0)});
1626 env(tx, ter{tecNO_PERMISSION});
1627 }
1628 },
1629 {.enableClawback = false});
1630
1631 testCase([this](
1632 Env& env,
1633 Account const& issuer,
1634 Account const& owner,
1635 Account const& depositor,
1636 Asset const& asset,
1637 Vault& vault,
1638 MPTTester& mptt) {
1639 testcase("MPT un-authorization");
1640 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1641 env(tx);
1642 env.close();
1643 tx = vault.deposit(
1644 {.depositor = depositor,
1645 .id = keylet.key,
1646 .amount = asset(1000)});
1647 env(tx);
1648 env.close();
1649
1650 mptt.authorize(
1651 {.account = issuer,
1652 .holder = depositor,
1653 .flags = tfMPTUnauthorize});
1654 env.close();
1655
1656 {
1657 auto tx = vault.withdraw(
1658 {.depositor = depositor,
1659 .id = keylet.key,
1660 .amount = asset(100)});
1661 env(tx, ter(tecNO_AUTH));
1662
1663 // Withdrawal to other (authorized) accounts works
1664 tx[sfDestination] = issuer.human();
1665 env(tx);
1666 tx[sfDestination] = owner.human();
1667 env(tx);
1668 env.close();
1669 }
1670
1671 {
1672 // Cannot deposit some more
1673 auto tx = vault.deposit(
1674 {.depositor = depositor,
1675 .id = keylet.key,
1676 .amount = asset(100)});
1677 env(tx, ter(tecNO_AUTH));
1678 }
1679
1680 // Clawback works
1681 tx = vault.clawback(
1682 {.issuer = issuer,
1683 .id = keylet.key,
1684 .holder = depositor,
1685 .amount = asset(800)});
1686 env(tx);
1687
1688 env(vault.del({.owner = owner, .id = keylet.key}));
1689 });
1690
1691 testCase([this](
1692 Env& env,
1693 Account const& issuer,
1694 Account const& owner,
1695 Account const& depositor,
1696 Asset const& asset,
1697 Vault& vault,
1698 MPTTester& mptt) {
1699 testcase("MPT lock of vault pseudo-account");
1700 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1701 env(tx);
1702 env.close();
1703
1704 auto const vaultAccount =
1705 [&env, keylet = keylet, this]() -> AccountID {
1706 auto const vault = env.le(keylet);
1707 BEAST_EXPECT(vault != nullptr);
1708 return vault->at(sfAccount);
1709 }();
1710
1711 tx = vault.deposit(
1712 {.depositor = depositor,
1713 .id = keylet.key,
1714 .amount = asset(100)});
1715 env(tx);
1716 env.close();
1717
1718 tx = [&]() {
1719 Json::Value jv;
1720 jv[jss::Account] = issuer.human();
1721 jv[sfMPTokenIssuanceID] =
1722 to_string(asset.get<MPTIssue>().getMptID());
1723 jv[jss::Holder] = toBase58(vaultAccount);
1724 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
1725 jv[jss::Flags] = tfMPTLock;
1726 return jv;
1727 }();
1728 env(tx);
1729 env.close();
1730
1731 tx = vault.deposit(
1732 {.depositor = depositor,
1733 .id = keylet.key,
1734 .amount = asset(100)});
1735 env(tx, ter(tecLOCKED));
1736
1737 tx = vault.withdraw(
1738 {.depositor = depositor,
1739 .id = keylet.key,
1740 .amount = asset(100)});
1741 env(tx, ter(tecLOCKED));
1742
1743 // Clawback works, even when locked
1744 tx = vault.clawback(
1745 {.issuer = issuer,
1746 .id = keylet.key,
1747 .holder = depositor,
1748 .amount = asset(100)});
1749 env(tx);
1750
1751 // Can delete an empty vault even when asset is locked.
1752 tx = vault.del({.owner = owner, .id = keylet.key});
1753 env(tx);
1754 });
1755
1756 {
1757 testcase("MPT shares to a vault");
1758
1759 Env env{*this, supported_amendments() | featureSingleAssetVault};
1760 Account owner{"owner"};
1761 Account issuer{"issuer"};
1762 env.fund(XRP(1000000), owner, issuer);
1763 env.close();
1764 Vault vault{env};
1765
1766 MPTTester mptt{env, issuer, mptInitNoFund};
1767 mptt.create(
1770 mptt.authorize({.account = owner});
1771 mptt.authorize({.account = issuer, .holder = owner});
1772 PrettyAsset asset = mptt.issuanceID();
1773 env(pay(issuer, owner, asset(100)));
1774 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
1775 env(tx1);
1776 env.close();
1777
1778 auto const shares = [&env, keylet = k1, this]() -> Asset {
1779 auto const vault = env.le(keylet);
1780 BEAST_EXPECT(vault != nullptr);
1781 return MPTIssue(vault->at(sfShareMPTID));
1782 }();
1783
1784 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
1785 env(tx2, ter{tecWRONG_ASSET});
1786 env.close();
1787 }
1788 }
1789
1790 void
1792 {
1793 using namespace test::jtx;
1794
1795 auto testCase =
1796 [&, this](
1797 std::function<void(
1798 Env & env,
1799 Account const& owner,
1800 Account const& issuer,
1801 Account const& charlie,
1802 std::function<AccountID(ripple::Keylet)> vaultAccount,
1803 Vault& vault,
1804 PrettyAsset const& asset,
1805 std::function<MPTID(ripple::Keylet)> issuanceId,
1807 test) {
1808 Env env{
1809 *this, supported_amendments() | featureSingleAssetVault};
1810 Account const owner{"owner"};
1811 Account const issuer{"issuer"};
1812 Account const charlie{"charlie"};
1813 Vault vault{env};
1814 env.fund(XRP(1000), issuer, owner, charlie);
1815 env(fset(issuer, asfAllowTrustLineClawback));
1816 env.close();
1817
1818 PrettyAsset const asset = issuer["IOU"];
1819 env.trust(asset(1000), owner);
1820 env(pay(issuer, owner, asset(200)));
1821 env(rate(issuer, 1.25));
1822 env.close();
1823
1824 auto const [tx, keylet] =
1825 vault.create({.owner = owner, .asset = asset});
1826 env(tx);
1827 env.close();
1828
1829 auto const vaultAccount =
1830 [&env](ripple::Keylet keylet) -> AccountID {
1831 return env.le(keylet)->at(sfAccount);
1832 };
1833 auto const issuanceId = [&env](ripple::Keylet keylet) -> MPTID {
1834 return env.le(keylet)->at(sfShareMPTID);
1835 };
1836 auto const vaultBalance = //
1837 [&env, &vaultAccount, issue = asset.raw().get<Issue>()](
1838 ripple::Keylet keylet) -> PrettyAmount {
1839 auto const account = vaultAccount(keylet);
1840 auto const sle = env.le(keylet::line(account, issue));
1841 if (sle == nullptr)
1842 return {
1843 STAmount(issue, 0),
1844 env.lookup(issue.account).name()};
1845 auto amount = sle->getFieldAmount(sfBalance);
1846 amount.setIssuer(issue.account);
1847 if (account > issue.account)
1848 amount.negate();
1849 return {amount, env.lookup(issue.account).name()};
1850 };
1851
1852 test(
1853 env,
1854 owner,
1855 issuer,
1856 charlie,
1857 vaultAccount,
1858 vault,
1859 asset,
1860 issuanceId,
1861 vaultBalance);
1862 };
1863
1864 testCase([&, this](
1865 Env& env,
1866 Account const& owner,
1867 Account const& issuer,
1868 Account const&,
1869 auto vaultAccount,
1870 Vault& vault,
1871 PrettyAsset const& asset,
1872 auto&&...) {
1873 testcase("IOU cannot use different asset");
1874 PrettyAsset const foo = issuer["FOO"];
1875
1876 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1877 env(tx);
1878 env.close();
1879
1880 {
1881 // Cannot create new trustline to a vault
1882 auto tx = [&, account = vaultAccount(keylet)]() {
1883 Json::Value jv;
1884 jv[jss::Account] = issuer.human();
1885 {
1886 auto& ja = jv[jss::LimitAmount] =
1887 foo(0).value().getJson(JsonOptions::none);
1888 ja[jss::issuer] = toBase58(account);
1889 }
1890 jv[jss::TransactionType] = jss::TrustSet;
1891 jv[jss::Flags] = tfSetFreeze;
1892 return jv;
1893 }();
1894 env(tx, ter{tecNO_PERMISSION});
1895 env.close();
1896 }
1897
1898 {
1899 auto tx = vault.deposit(
1900 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
1901 env(tx, ter{tecWRONG_ASSET});
1902 env.close();
1903 }
1904
1905 {
1906 auto tx = vault.withdraw(
1907 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
1908 env(tx, ter{tecWRONG_ASSET});
1909 env.close();
1910 }
1911
1912 env(vault.del({.owner = owner, .id = keylet.key}));
1913 env.close();
1914 });
1915
1916 testCase([&, this](
1917 Env& env,
1918 Account const& owner,
1919 Account const& issuer,
1920 Account const& charlie,
1921 auto vaultAccount,
1922 Vault& vault,
1923 PrettyAsset const& asset,
1924 auto issuanceId,
1925 auto) {
1926 testcase("IOU frozen trust line to vault account");
1927
1928 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1929 env(tx);
1930 env.close();
1931
1932 env(vault.deposit(
1933 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
1934 env.close();
1935
1936 Asset const share = Asset(issuanceId(keylet));
1937
1938 // Freeze the trustline to the vault
1939 auto trustSet = [&, account = vaultAccount(keylet)]() {
1940 Json::Value jv;
1941 jv[jss::Account] = issuer.human();
1942 {
1943 auto& ja = jv[jss::LimitAmount] =
1944 asset(0).value().getJson(JsonOptions::none);
1945 ja[jss::issuer] = toBase58(account);
1946 }
1947 jv[jss::TransactionType] = jss::TrustSet;
1948 jv[jss::Flags] = tfSetFreeze;
1949 return jv;
1950 }();
1951 env(trustSet);
1952 env.close();
1953
1954 {
1955 // Note, the "frozen" state of the trust line to vault account
1956 // is reported as "locked" state of the vault shares, because
1957 // this state is attached to shares by means of the transitive
1958 // isFrozen.
1959 auto tx = vault.deposit(
1960 {.depositor = owner,
1961 .id = keylet.key,
1962 .amount = asset(80)});
1963 env(tx, ter{tecLOCKED});
1964 }
1965
1966 {
1967 auto tx = vault.withdraw(
1968 {.depositor = owner,
1969 .id = keylet.key,
1970 .amount = asset(100)});
1971 env(tx, ter{tecLOCKED});
1972
1973 // also when trying to withdraw to a 3rd party
1974 tx[sfDestination] = charlie.human();
1975 env(tx, ter{tecLOCKED});
1976 env.close();
1977 }
1978
1979 {
1980 // Clawback works, even when locked
1981 auto tx = vault.clawback(
1982 {.issuer = issuer,
1983 .id = keylet.key,
1984 .holder = owner,
1985 .amount = asset(50)});
1986 env(tx);
1987 env.close();
1988 }
1989
1990 // Clear the frozen state
1991 trustSet[jss::Flags] = tfClearFreeze;
1992 env(trustSet);
1993 env.close();
1994
1995 env(vault.withdraw(
1996 {.depositor = owner, .id = keylet.key, .amount = share(50)}));
1997
1998 env(vault.del({.owner = owner, .id = keylet.key}));
1999 env.close();
2000 });
2001
2002 testCase([&, this](
2003 Env& env,
2004 Account const& owner,
2005 Account const& issuer,
2006 Account const& charlie,
2007 auto,
2008 Vault& vault,
2009 PrettyAsset const& asset,
2010 auto issuanceId,
2011 auto vaultBalance) {
2012 testcase("IOU transfer fees not applied");
2013
2014 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2015 env(tx);
2016 env.close();
2017
2018 env(vault.deposit(
2019 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2020 env.close();
2021
2022 auto const issue = asset.raw().get<Issue>();
2023 Asset const share = Asset(issuanceId(keylet));
2024
2025 // transfer fees ignored on deposit
2026 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2027 BEAST_EXPECT(vaultBalance(keylet) == asset(100));
2028
2029 {
2030 auto tx = vault.clawback(
2031 {.issuer = issuer,
2032 .id = keylet.key,
2033 .holder = owner,
2034 .amount = asset(50)});
2035 env(tx);
2036 env.close();
2037 }
2038
2039 // transfer fees ignored on clawback
2040 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2041 BEAST_EXPECT(vaultBalance(keylet) == asset(50));
2042
2043 env(vault.withdraw(
2044 {.depositor = owner, .id = keylet.key, .amount = share(20)}));
2045
2046 // transfer fees ignored on withdraw
2047 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2048 BEAST_EXPECT(vaultBalance(keylet) == asset(30));
2049
2050 {
2051 auto tx = vault.withdraw(
2052 {.depositor = owner,
2053 .id = keylet.key,
2054 .amount = share(30)});
2055 tx[sfDestination] = charlie.human();
2056 env(tx);
2057 }
2058
2059 // transfer fees ignored on withdraw to 3rd party
2060 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2061 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2062 BEAST_EXPECT(vaultBalance(keylet) == asset(0));
2063
2064 env(vault.del({.owner = owner, .id = keylet.key}));
2065 env.close();
2066 });
2067
2068 testCase([&, this](
2069 Env& env,
2070 Account const& owner,
2071 Account const& issuer,
2072 Account const& charlie,
2073 auto,
2074 Vault& vault,
2075 PrettyAsset const& asset,
2076 auto&&...) {
2077 testcase("IOU frozen trust line to depositor");
2078
2079 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2080 env(tx);
2081 env.close();
2082
2083 env(vault.deposit(
2084 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2085 env.close();
2086
2087 // Withdraw to 3rd party works
2088 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
2089 auto tx = vault.withdraw(
2090 {.depositor = owner,
2091 .id = keylet.key,
2092 .amount = asset(10)});
2093 tx[sfDestination] = charlie.human();
2094 return tx;
2095 }(keylet);
2096 env(withdrawToCharlie);
2097
2098 // Freeze the owner
2099 env(trust(issuer, asset(0), owner, tfSetFreeze));
2100 env.close();
2101
2102 // Cannot withdraw
2103 auto const withdraw = vault.withdraw(
2104 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
2105 env(withdraw, ter{tecFROZEN});
2106
2107 // Cannot withdraw to 3rd party
2108 env(withdrawToCharlie, ter{tecLOCKED});
2109 env.close();
2110
2111 {
2112 // Cannot deposit some more
2113 auto tx = vault.deposit(
2114 {.depositor = owner,
2115 .id = keylet.key,
2116 .amount = asset(10)});
2117 env(tx, ter{tecFROZEN});
2118 }
2119
2120 {
2121 // Clawback still works
2122 auto tx = vault.clawback(
2123 {.issuer = issuer,
2124 .id = keylet.key,
2125 .holder = owner,
2126 .amount = asset(0)});
2127 env(tx);
2128 env.close();
2129 }
2130
2131 env(vault.del({.owner = owner, .id = keylet.key}));
2132 env.close();
2133 });
2134
2135 testCase([&, this](
2136 Env& env,
2137 Account const& owner,
2138 Account const& issuer,
2139 Account const& charlie,
2140 auto,
2141 Vault& vault,
2142 PrettyAsset const& asset,
2143 auto&&...) {
2144 testcase("IOU frozen trust line to 3rd party");
2145
2146 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2147 env(tx);
2148 env.close();
2149
2150 env(vault.deposit(
2151 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2152 env.close();
2153
2154 // Withdraw to 3rd party works
2155 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
2156 auto tx = vault.withdraw(
2157 {.depositor = owner,
2158 .id = keylet.key,
2159 .amount = asset(10)});
2160 tx[sfDestination] = charlie.human();
2161 return tx;
2162 }(keylet);
2163 env(withdrawToCharlie);
2164
2165 // Freeze the 3rd party
2166 env(trust(issuer, asset(0), charlie, tfSetFreeze));
2167 env.close();
2168
2169 // Can withdraw
2170 auto const withdraw = vault.withdraw(
2171 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
2172 env(withdraw);
2173 env.close();
2174
2175 // Cannot withdraw to 3rd party
2176 env(withdrawToCharlie, ter{tecFROZEN});
2177 env.close();
2178
2179 env(vault.clawback(
2180 {.issuer = issuer,
2181 .id = keylet.key,
2182 .holder = owner,
2183 .amount = asset(0)}));
2184 env.close();
2185
2186 env(vault.del({.owner = owner, .id = keylet.key}));
2187 env.close();
2188 });
2189
2190 testCase([&, this](
2191 Env& env,
2192 Account const& owner,
2193 Account const& issuer,
2194 Account const& charlie,
2195 auto,
2196 Vault& vault,
2197 PrettyAsset const& asset,
2198 auto&&...) {
2199 testcase("IOU global freeze");
2200
2201 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2202 env(tx);
2203 env.close();
2204
2205 env(vault.deposit(
2206 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2207 env.close();
2208
2209 env(fset(issuer, asfGlobalFreeze));
2210 env.close();
2211
2212 {
2213 // Cannot withdraw
2214 auto tx = vault.withdraw(
2215 {.depositor = owner,
2216 .id = keylet.key,
2217 .amount = asset(10)});
2218 env(tx, ter{tecFROZEN});
2219
2220 // Cannot withdraw to 3rd party
2221 tx[sfDestination] = charlie.human();
2222 env(tx, ter{tecFROZEN});
2223 env.close();
2224
2225 // Cannot deposit some more
2226 tx = vault.deposit(
2227 {.depositor = owner,
2228 .id = keylet.key,
2229 .amount = asset(10)});
2230
2231 env(tx, ter{tecFROZEN});
2232 }
2233
2234 // Clawback is permitted
2235 env(vault.clawback(
2236 {.issuer = issuer,
2237 .id = keylet.key,
2238 .holder = owner,
2239 .amount = asset(0)}));
2240 env.close();
2241
2242 env(vault.del({.owner = owner, .id = keylet.key}));
2243 env.close();
2244 });
2245 }
2246
2247 void
2249 {
2250 using namespace test::jtx;
2251
2252 testcase("private vault");
2253
2254 Env env{*this, supported_amendments() | featureSingleAssetVault};
2255 Account issuer{"issuer"};
2256 Account owner{"owner"};
2257 Account depositor{"depositor"};
2258 Account charlie{"charlie"};
2259 Account pdOwner{"pdOwner"};
2260 Account credIssuer1{"credIssuer1"};
2261 Account credIssuer2{"credIssuer2"};
2262 std::string const credType = "credential";
2263 Vault vault{env};
2264 env.fund(
2265 XRP(1000),
2266 issuer,
2267 owner,
2268 depositor,
2269 charlie,
2270 pdOwner,
2271 credIssuer1,
2272 credIssuer2);
2273 env.close();
2274 env(fset(issuer, asfAllowTrustLineClawback));
2275 env.close();
2276 env.require(flags(issuer, asfAllowTrustLineClawback));
2277
2278 PrettyAsset asset = issuer["IOU"];
2279 env.trust(asset(1000), owner);
2280 env(pay(issuer, owner, asset(500)));
2281 env.trust(asset(1000), depositor);
2282 env(pay(issuer, depositor, asset(500)));
2283 env.trust(asset(1000), charlie);
2284 env(pay(issuer, charlie, asset(5)));
2285 env.close();
2286
2287 auto [tx, keylet] = vault.create(
2288 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
2289 env(tx);
2290 env.close();
2291 BEAST_EXPECT(env.le(keylet));
2292
2293 {
2294 testcase("private vault owner can deposit");
2295 auto tx = vault.deposit(
2296 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
2297 env(tx);
2298 }
2299
2300 {
2301 testcase("private vault depositor not authorized yet");
2302 auto tx = vault.deposit(
2303 {.depositor = depositor,
2304 .id = keylet.key,
2305 .amount = asset(50)});
2306 env(tx, ter{tecNO_AUTH});
2307 }
2308
2309 {
2310 testcase("private vault cannot set non-existing domain");
2311 auto tx = vault.set({.owner = owner, .id = keylet.key});
2312 tx[sfDomainID] = to_string(base_uint<256>(42ul));
2313 env(tx, ter{tecOBJECT_NOT_FOUND});
2314 }
2315
2316 {
2317 testcase("private vault set domainId");
2318
2319 {
2320 pdomain::Credentials const credentials1{
2321 {.issuer = credIssuer1, .credType = credType}};
2322
2323 env(pdomain::setTx(pdOwner, credentials1));
2324 auto const domainId1 = [&]() {
2325 auto tx = env.tx()->getJson(JsonOptions::none);
2326 return pdomain::getNewDomain(env.meta());
2327 }();
2328
2329 auto tx = vault.set({.owner = owner, .id = keylet.key});
2330 tx[sfDomainID] = to_string(domainId1);
2331 env(tx);
2332 env.close();
2333
2334 // Update domain second time, should be harmless
2335 env(tx);
2336 env.close();
2337 }
2338
2339 {
2340 pdomain::Credentials const credentials{
2341 {.issuer = credIssuer1, .credType = credType},
2342 {.issuer = credIssuer2, .credType = credType}};
2343
2344 env(pdomain::setTx(pdOwner, credentials));
2345 auto const domainId = [&]() {
2346 auto tx = env.tx()->getJson(JsonOptions::none);
2347 return pdomain::getNewDomain(env.meta());
2348 }();
2349
2350 auto tx = vault.set({.owner = owner, .id = keylet.key});
2351 tx[sfDomainID] = to_string(domainId);
2352 env(tx);
2353 env.close();
2354 }
2355 }
2356
2357 {
2358 testcase("private vault depositor still not authorized");
2359 auto tx = vault.deposit(
2360 {.depositor = depositor,
2361 .id = keylet.key,
2362 .amount = asset(50)});
2363 env(tx, ter{tecNO_AUTH});
2364 env.close();
2365 }
2366
2367 auto const credKeylet =
2368 credentials::keylet(depositor, credIssuer1, credType);
2369 {
2370 testcase("private vault depositor now authorized");
2371 env(credentials::create(depositor, credIssuer1, credType));
2372 env(credentials::accept(depositor, credIssuer1, credType));
2373 env(credentials::create(charlie, credIssuer1, credType));
2374 // charlie's credential not accepted
2375 env.close();
2376 auto credSle = env.le(credKeylet);
2377 BEAST_EXPECT(credSle != nullptr);
2378
2379 auto tx = vault.deposit(
2380 {.depositor = depositor,
2381 .id = keylet.key,
2382 .amount = asset(50)});
2383 env(tx);
2384 env.close();
2385
2386 tx = vault.deposit(
2387 {.depositor = charlie, .id = keylet.key, .amount = asset(50)});
2388 env(tx, ter{tecNO_AUTH});
2389 env.close();
2390 }
2391
2392 {
2393 testcase("private vault depositor lost authorization");
2394 env(credentials::deleteCred(
2395 credIssuer1, depositor, credIssuer1, credType));
2396 env(credentials::deleteCred(
2397 credIssuer1, charlie, credIssuer1, credType));
2398 env.close();
2399 auto credSle = env.le(credKeylet);
2400 BEAST_EXPECT(credSle == nullptr);
2401
2402 auto tx = vault.deposit(
2403 {.depositor = depositor,
2404 .id = keylet.key,
2405 .amount = asset(50)});
2406 env(tx, ter{tecNO_AUTH});
2407 env.close();
2408 }
2409
2410 auto const shares = [&env, keylet = keylet, this]() -> Asset {
2411 auto const vault = env.le(keylet);
2412 BEAST_EXPECT(vault != nullptr);
2413 return MPTIssue(vault->at(sfShareMPTID));
2414 }();
2415
2416 {
2417 testcase("private vault expired authorization");
2418 uint32_t const closeTime = env.current()
2419 ->info()
2420 .parentCloseTime.time_since_epoch()
2421 .count();
2422 {
2423 auto tx0 =
2424 credentials::create(depositor, credIssuer2, credType);
2425 tx0[sfExpiration] = closeTime + 20;
2426 env(tx0);
2427 tx0 = credentials::create(charlie, credIssuer2, credType);
2428 tx0[sfExpiration] = closeTime + 20;
2429 env(tx0);
2430 env.close();
2431
2432 env(credentials::accept(depositor, credIssuer2, credType));
2433 env(credentials::accept(charlie, credIssuer2, credType));
2434 env.close();
2435 }
2436
2437 {
2438 auto tx1 = vault.deposit(
2439 {.depositor = depositor,
2440 .id = keylet.key,
2441 .amount = asset(50)});
2442 env(tx1);
2443 env.close();
2444
2445 auto const tokenKeylet = keylet::mptoken(
2446 shares.get<MPTIssue>().getMptID(), depositor.id());
2447 BEAST_EXPECT(env.le(tokenKeylet) != nullptr);
2448 }
2449
2450 {
2451 // time advance
2452 env.close();
2453 env.close();
2454 env.close();
2455
2456 auto const credsKeylet =
2457 credentials::keylet(depositor, credIssuer2, credType);
2458 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
2459
2460 auto tx2 = vault.deposit(
2461 {.depositor = depositor,
2462 .id = keylet.key,
2463 .amount = asset(1)});
2464 env(tx2, ter{tecEXPIRED});
2465 env.close();
2466
2467 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
2468 }
2469
2470 {
2471 auto const credsKeylet =
2472 credentials::keylet(charlie, credIssuer2, credType);
2473 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
2474 auto const tokenKeylet = keylet::mptoken(
2475 shares.get<MPTIssue>().getMptID(), charlie.id());
2476 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
2477
2478 auto tx3 = vault.deposit(
2479 {.depositor = charlie,
2480 .id = keylet.key,
2481 .amount = asset(2)});
2482 env(tx3, ter{tecEXPIRED});
2483
2484 env.close();
2485 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
2486 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
2487 }
2488 }
2489
2490 {
2491 testcase("private vault reset domainId");
2492 auto tx = vault.set({.owner = owner, .id = keylet.key});
2493 tx[sfDomainID] = "0";
2494 env(tx);
2495 env.close();
2496
2497 tx = vault.deposit(
2498 {.depositor = depositor,
2499 .id = keylet.key,
2500 .amount = asset(50)});
2501 env(tx, ter{tecNO_AUTH});
2502 env.close();
2503
2504 tx = vault.withdraw(
2505 {.depositor = depositor,
2506 .id = keylet.key,
2507 .amount = asset(50)});
2508 env(tx);
2509
2510 tx = vault.clawback(
2511 {.issuer = issuer,
2512 .id = keylet.key,
2513 .holder = depositor,
2514 .amount = asset(0)});
2515 env(tx);
2516
2517 tx = vault.clawback(
2518 {.issuer = issuer,
2519 .id = keylet.key,
2520 .holder = owner,
2521 .amount = asset(0)});
2522 env(tx);
2523
2524 tx = vault.del({
2525 .owner = owner,
2526 .id = keylet.key,
2527 });
2528 env(tx);
2529 }
2530 }
2531
2532 void
2534 {
2535 using namespace test::jtx;
2536
2537 testcase("private XRP vault");
2538
2539 Env env{*this, supported_amendments() | featureSingleAssetVault};
2540 Account owner{"owner"};
2541 Account depositor{"depositor"};
2542 Account alice{"charlie"};
2543 std::string const credType = "credential";
2544 Vault vault{env};
2545 env.fund(XRP(100000), owner, depositor, alice);
2546 env.close();
2547
2548 PrettyAsset asset = xrpIssue();
2549 auto [tx, keylet] = vault.create(
2550 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
2551 env(tx);
2552 env.close();
2553
2554 auto const [vaultAccount, issuanceId] =
2555 [&env, keylet = keylet, this]() -> std::tuple<AccountID, uint192> {
2556 auto const vault = env.le(keylet);
2557 BEAST_EXPECT(vault != nullptr);
2558 return {vault->at(sfAccount), vault->at(sfShareMPTID)};
2559 }();
2560 BEAST_EXPECT(env.le(keylet::account(vaultAccount)));
2561 BEAST_EXPECT(env.le(keylet::mptIssuance(issuanceId)));
2562 PrettyAsset shares{issuanceId};
2563
2564 {
2565 testcase("private XRP vault owner can deposit");
2566 auto tx = vault.deposit(
2567 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
2568 env(tx);
2569 }
2570
2571 {
2572 testcase("private XRP vault cannot pay shares to depositor yet");
2573 env(pay(owner, depositor, shares(1)), ter{tecNO_AUTH});
2574 }
2575
2576 {
2577 testcase("private XRP vault depositor not authorized yet");
2578 auto tx = vault.deposit(
2579 {.depositor = depositor,
2580 .id = keylet.key,
2581 .amount = asset(50)});
2582 env(tx, ter{tecNO_AUTH});
2583 }
2584
2585 {
2586 testcase("private XRP vault set DomainID");
2587 pdomain::Credentials const credentials{
2588 {.issuer = owner, .credType = credType}};
2589
2590 env(pdomain::setTx(owner, credentials));
2591 auto const domainId = [&]() {
2592 auto tx = env.tx()->getJson(JsonOptions::none);
2593 return pdomain::getNewDomain(env.meta());
2594 }();
2595
2596 auto tx = vault.set({.owner = owner, .id = keylet.key});
2597 tx[sfDomainID] = to_string(domainId);
2598 env(tx);
2599 env.close();
2600 }
2601
2602 auto const credKeylet = credentials::keylet(depositor, owner, credType);
2603 {
2604 testcase("private XRP vault depositor now authorized");
2605 env(credentials::create(depositor, owner, credType));
2606 env(credentials::accept(depositor, owner, credType));
2607 env.close();
2608
2609 BEAST_EXPECT(env.le(credKeylet));
2610 auto tx = vault.deposit(
2611 {.depositor = depositor,
2612 .id = keylet.key,
2613 .amount = asset(50)});
2614 env(tx);
2615 env.close();
2616 }
2617
2618 {
2619 testcase("private XRP vault can pay shares to depositor");
2620 env(pay(owner, depositor, shares(1)));
2621 }
2622
2623 {
2624 testcase("private XRP vault cannot pay shares to 3rd party");
2625 Json::Value jv;
2626 jv[sfAccount] = alice.human();
2627 jv[sfTransactionType] = jss::MPTokenAuthorize;
2628 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
2629 env(jv);
2630 env.close();
2631
2632 env(pay(owner, alice, shares(1)), ter{tecNO_AUTH});
2633 }
2634 }
2635
2636 void
2638 {
2639 using namespace test::jtx;
2640
2641 testcase("failed pseudo-account allocation");
2642 Env env{*this, supported_amendments() | featureSingleAssetVault};
2643 Account const owner{"owner"};
2644 Vault vault{env};
2645 env.fund(XRP(1000), owner);
2646
2647 auto const keylet = keylet::vault(owner.id(), env.seq(owner));
2648 for (int i = 0; i < 256; ++i)
2649 {
2650 AccountID const accountId =
2651 ripple::pseudoAccountAddress(*env.current(), keylet.key);
2652
2653 env(pay(env.master.id(), accountId, XRP(1000)),
2654 seq(autofill),
2655 fee(autofill),
2656 sig(autofill));
2657 }
2658
2659 auto [tx, keylet1] =
2660 vault.create({.owner = owner, .asset = xrpIssue()});
2661 BEAST_EXPECT(keylet.key == keylet1.key);
2662 env(tx, ter{terADDRESS_COLLISION});
2663 }
2664
2665 void
2667 {
2668 using namespace test::jtx;
2669
2670 testcase("RPC");
2671 Env env{*this, supported_amendments() | featureSingleAssetVault};
2672 Account const owner{"owner"};
2673 Account const issuer{"issuer"};
2674 Vault vault{env};
2675 env.fund(XRP(1000), issuer, owner);
2676 env.close();
2677
2678 PrettyAsset asset = issuer["IOU"];
2679 env.trust(asset(1000), owner);
2680 env(pay(issuer, owner, asset(200)));
2681 env.close();
2682
2683 auto const sequence = env.seq(owner);
2684 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2685 env(tx);
2686 env.close();
2687
2688 // Set some fields
2689 {
2690 auto tx1 = vault.deposit(
2691 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
2692 env(tx1);
2693
2694 auto tx2 = vault.set({.owner = owner, .id = keylet.key});
2695 tx2[sfAssetsMaximum] = asset(1000).number();
2696 env(tx2);
2697 env.close();
2698 }
2699
2700 auto const sleVault = [&env, keylet = keylet, this]() {
2701 auto const vault = env.le(keylet);
2702 BEAST_EXPECT(vault != nullptr);
2703 return vault;
2704 }();
2705
2706 auto const check = [&, keylet = keylet, sle = sleVault, this](
2707 Json::Value const& vault,
2708 Json::Value const& issuance = Json::nullValue) {
2709 BEAST_EXPECT(vault.isObject());
2710
2711 constexpr auto checkString =
2712 [](auto& node, SField const& field, std::string v) -> bool {
2713 return node.isMember(field.fieldName) &&
2714 node[field.fieldName].isString() &&
2715 node[field.fieldName] == v;
2716 };
2717 constexpr auto checkObject =
2718 [](auto& node, SField const& field, Json::Value v) -> bool {
2719 return node.isMember(field.fieldName) &&
2720 node[field.fieldName].isObject() &&
2721 node[field.fieldName] == v;
2722 };
2723 constexpr auto checkInt =
2724 [](auto& node, SField const& field, int v) -> bool {
2725 return node.isMember(field.fieldName) &&
2726 ((node[field.fieldName].isInt() &&
2727 node[field.fieldName] == Json::Int(v)) ||
2728 (node[field.fieldName].isUInt() &&
2729 node[field.fieldName] == Json::UInt(v)));
2730 };
2731
2732 BEAST_EXPECT(vault["LedgerEntryType"].asString() == "Vault");
2733 BEAST_EXPECT(vault[jss::index].asString() == strHex(keylet.key));
2734 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
2735 // Ignore all other standard fields, this test doesn't care
2736
2737 BEAST_EXPECT(
2738 checkString(vault, sfAccount, toBase58(sle->at(sfAccount))));
2739 BEAST_EXPECT(
2740 checkObject(vault, sfAsset, to_json(sle->at(sfAsset))));
2741 BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
2742 BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
2743 BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
2744 BEAST_EXPECT(checkString(vault, sfLossUnrealized, "0"));
2745
2746 auto const strShareID = strHex(sle->at(sfShareMPTID));
2747 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
2748 BEAST_EXPECT(checkString(vault, sfOwner, toBase58(owner.id())));
2749 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
2750 BEAST_EXPECT(checkInt(
2751 vault, sfWithdrawalPolicy, vaultStrategyFirstComeFirstServe));
2752
2753 if (issuance.isObject())
2754 {
2755 BEAST_EXPECT(
2756 issuance["LedgerEntryType"].asString() ==
2757 "MPTokenIssuance");
2758 BEAST_EXPECT(
2759 issuance[jss::mpt_issuance_id].asString() == strShareID);
2760 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
2761 BEAST_EXPECT(checkInt(
2762 issuance,
2763 sfFlags,
2765 BEAST_EXPECT(checkString(issuance, sfOutstandingAmount, "50"));
2766 }
2767 };
2768
2769 {
2770 testcase("RPC ledger_entry selected by key");
2771 Json::Value jvParams;
2772 jvParams[jss::ledger_index] = jss::validated;
2773 jvParams[jss::vault] = strHex(keylet.key);
2774 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2775
2776 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
2777 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
2778 check(jvVault[jss::result][jss::node]);
2779 }
2780
2781 {
2782 testcase("RPC ledger_entry selected by owner and seq");
2783 Json::Value jvParams;
2784 jvParams[jss::ledger_index] = jss::validated;
2785 jvParams[jss::vault][jss::owner] = owner.human();
2786 jvParams[jss::vault][jss::seq] = sequence;
2787 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2788
2789 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
2790 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
2791 check(jvVault[jss::result][jss::node]);
2792 }
2793
2794 {
2795 testcase("RPC ledger_entry cannot find vault by key");
2796 Json::Value jvParams;
2797 jvParams[jss::ledger_index] = jss::validated;
2798 jvParams[jss::vault] = to_string(uint256(42));
2799 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2800 BEAST_EXPECT(
2801 jvVault[jss::result][jss::error].asString() == "entryNotFound");
2802 }
2803
2804 {
2805 testcase("RPC ledger_entry cannot find vault by owner and seq");
2806 Json::Value jvParams;
2807 jvParams[jss::ledger_index] = jss::validated;
2808 jvParams[jss::vault][jss::owner] = issuer.human();
2809 jvParams[jss::vault][jss::seq] = 1'000'000;
2810 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2811 BEAST_EXPECT(
2812 jvVault[jss::result][jss::error].asString() == "entryNotFound");
2813 }
2814
2815 {
2816 testcase("RPC ledger_entry malformed key");
2817 Json::Value jvParams;
2818 jvParams[jss::ledger_index] = jss::validated;
2819 jvParams[jss::vault] = 42;
2820 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2821 BEAST_EXPECT(
2822 jvVault[jss::result][jss::error].asString() ==
2823 "malformedRequest");
2824 }
2825
2826 {
2827 testcase("RPC ledger_entry malformed owner");
2828 Json::Value jvParams;
2829 jvParams[jss::ledger_index] = jss::validated;
2830 jvParams[jss::vault][jss::owner] = 42;
2831 jvParams[jss::vault][jss::seq] = sequence;
2832 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2833 BEAST_EXPECT(
2834 jvVault[jss::result][jss::error].asString() ==
2835 "malformedOwner");
2836 }
2837
2838 {
2839 testcase("RPC ledger_entry malformed seq");
2840 Json::Value jvParams;
2841 jvParams[jss::ledger_index] = jss::validated;
2842 jvParams[jss::vault][jss::owner] = issuer.human();
2843 jvParams[jss::vault][jss::seq] = "foo";
2844 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2845 BEAST_EXPECT(
2846 jvVault[jss::result][jss::error].asString() ==
2847 "malformedRequest");
2848 }
2849
2850 {
2851 testcase("RPC ledger_entry zero seq");
2852 Json::Value jvParams;
2853 jvParams[jss::ledger_index] = jss::validated;
2854 jvParams[jss::vault][jss::owner] = issuer.human();
2855 jvParams[jss::vault][jss::seq] = 0;
2856 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2857 BEAST_EXPECT(
2858 jvVault[jss::result][jss::error].asString() ==
2859 "malformedRequest");
2860 }
2861
2862 {
2863 testcase("RPC ledger_entry negative seq");
2864 Json::Value jvParams;
2865 jvParams[jss::ledger_index] = jss::validated;
2866 jvParams[jss::vault][jss::owner] = issuer.human();
2867 jvParams[jss::vault][jss::seq] = -1;
2868 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2869 BEAST_EXPECT(
2870 jvVault[jss::result][jss::error].asString() ==
2871 "malformedRequest");
2872 }
2873
2874 {
2875 testcase("RPC ledger_entry oversized seq");
2876 Json::Value jvParams;
2877 jvParams[jss::ledger_index] = jss::validated;
2878 jvParams[jss::vault][jss::owner] = issuer.human();
2879 jvParams[jss::vault][jss::seq] = 1e20;
2880 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2881 BEAST_EXPECT(
2882 jvVault[jss::result][jss::error].asString() ==
2883 "malformedRequest");
2884 }
2885
2886 {
2887 testcase("RPC ledger_entry bool seq");
2888 Json::Value jvParams;
2889 jvParams[jss::ledger_index] = jss::validated;
2890 jvParams[jss::vault][jss::owner] = issuer.human();
2891 jvParams[jss::vault][jss::seq] = true;
2892 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2893 BEAST_EXPECT(
2894 jvVault[jss::result][jss::error].asString() ==
2895 "malformedRequest");
2896 }
2897
2898 {
2899 testcase("RPC account_objects");
2900
2901 Json::Value jvParams;
2902 jvParams[jss::account] = owner.human();
2903 jvParams[jss::type] = jss::vault;
2904 auto jv = env.rpc(
2905 "json", "account_objects", to_string(jvParams))[jss::result];
2906
2907 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
2908 check(jv[jss::account_objects][0u]);
2909 }
2910
2911 {
2912 testcase("RPC ledger_data");
2913
2914 Json::Value jvParams;
2915 jvParams[jss::ledger_index] = jss::validated;
2916 jvParams[jss::binary] = false;
2917 jvParams[jss::type] = jss::vault;
2918 Json::Value jv =
2919 env.rpc("json", "ledger_data", to_string(jvParams));
2920 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
2921 check(jv[jss::result][jss::state][0u]);
2922 }
2923
2924 {
2925 testcase("RPC vault_info command line");
2926 Json::Value jv =
2927 env.rpc("vault_info", strHex(keylet.key), "validated");
2928
2929 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
2930 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
2931 check(
2932 jv[jss::result][jss::vault],
2933 jv[jss::result][jss::vault][jss::shares]);
2934 }
2935
2936 {
2937 testcase("RPC vault_info json");
2938 Json::Value jvParams;
2939 jvParams[jss::ledger_index] = jss::validated;
2940 jvParams[jss::vault_id] = strHex(keylet.key);
2941 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
2942
2943 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
2944 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
2945 check(
2946 jv[jss::result][jss::vault],
2947 jv[jss::result][jss::vault][jss::shares]);
2948 }
2949
2950 {
2951 testcase("RPC vault_info invalid vault_id");
2952 Json::Value jvParams;
2953 jvParams[jss::ledger_index] = jss::validated;
2954 jvParams[jss::vault_id] = "foobar";
2955 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
2956 BEAST_EXPECT(
2957 jv[jss::result][jss::error].asString() == "malformedRequest");
2958 }
2959
2960 {
2961 testcase("RPC vault_info json invalid index");
2962 Json::Value jvParams;
2963 jvParams[jss::ledger_index] = jss::validated;
2964 jvParams[jss::vault_id] = 0;
2965 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
2966 BEAST_EXPECT(
2967 jv[jss::result][jss::error].asString() == "malformedRequest");
2968 }
2969
2970 {
2971 testcase("RPC vault_info json by owner and sequence");
2972 Json::Value jvParams;
2973 jvParams[jss::ledger_index] = jss::validated;
2974 jvParams[jss::owner] = owner.human();
2975 jvParams[jss::seq] = sequence;
2976 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
2977
2978 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
2979 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
2980 check(
2981 jv[jss::result][jss::vault],
2982 jv[jss::result][jss::vault][jss::shares]);
2983 }
2984
2985 {
2986 testcase("RPC vault_info json malformed sequence");
2987 Json::Value jvParams;
2988 jvParams[jss::ledger_index] = jss::validated;
2989 jvParams[jss::owner] = owner.human();
2990 jvParams[jss::seq] = "foobar";
2991 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
2992 BEAST_EXPECT(
2993 jv[jss::result][jss::error].asString() == "malformedRequest");
2994 }
2995
2996 {
2997 testcase("RPC vault_info json invalid sequence");
2998 Json::Value jvParams;
2999 jvParams[jss::ledger_index] = jss::validated;
3000 jvParams[jss::owner] = owner.human();
3001 jvParams[jss::seq] = 0;
3002 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3003 BEAST_EXPECT(
3004 jv[jss::result][jss::error].asString() == "malformedRequest");
3005 }
3006
3007 {
3008 testcase("RPC vault_info json negative sequence");
3009 Json::Value jvParams;
3010 jvParams[jss::ledger_index] = jss::validated;
3011 jvParams[jss::owner] = owner.human();
3012 jvParams[jss::seq] = -1;
3013 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3014 BEAST_EXPECT(
3015 jv[jss::result][jss::error].asString() == "malformedRequest");
3016 }
3017
3018 {
3019 testcase("RPC vault_info json oversized sequence");
3020 Json::Value jvParams;
3021 jvParams[jss::ledger_index] = jss::validated;
3022 jvParams[jss::owner] = owner.human();
3023 jvParams[jss::seq] = 1e20;
3024 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3025 BEAST_EXPECT(
3026 jv[jss::result][jss::error].asString() == "malformedRequest");
3027 }
3028
3029 {
3030 testcase("RPC vault_info json bool sequence");
3031 Json::Value jvParams;
3032 jvParams[jss::ledger_index] = jss::validated;
3033 jvParams[jss::owner] = owner.human();
3034 jvParams[jss::seq] = true;
3035 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3036 BEAST_EXPECT(
3037 jv[jss::result][jss::error].asString() == "malformedRequest");
3038 }
3039
3040 {
3041 testcase("RPC vault_info json malformed owner");
3042 Json::Value jvParams;
3043 jvParams[jss::ledger_index] = jss::validated;
3044 jvParams[jss::owner] = "foobar";
3045 jvParams[jss::seq] = sequence;
3046 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3047 BEAST_EXPECT(
3048 jv[jss::result][jss::error].asString() == "malformedRequest");
3049 }
3050
3051 {
3052 testcase("RPC vault_info json invalid combination only owner");
3053 Json::Value jvParams;
3054 jvParams[jss::ledger_index] = jss::validated;
3055 jvParams[jss::owner] = owner.human();
3056 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3057 BEAST_EXPECT(
3058 jv[jss::result][jss::error].asString() == "malformedRequest");
3059 }
3060
3061 {
3062 testcase("RPC vault_info json invalid combination only seq");
3063 Json::Value jvParams;
3064 jvParams[jss::ledger_index] = jss::validated;
3065 jvParams[jss::seq] = sequence;
3066 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3067 BEAST_EXPECT(
3068 jv[jss::result][jss::error].asString() == "malformedRequest");
3069 }
3070
3071 {
3072 testcase("RPC vault_info json invalid combination seq vault_id");
3073 Json::Value jvParams;
3074 jvParams[jss::ledger_index] = jss::validated;
3075 jvParams[jss::vault_id] = strHex(keylet.key);
3076 jvParams[jss::seq] = sequence;
3077 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3078 BEAST_EXPECT(
3079 jv[jss::result][jss::error].asString() == "malformedRequest");
3080 }
3081
3082 {
3083 testcase("RPC vault_info json invalid combination owner vault_id");
3084 Json::Value jvParams;
3085 jvParams[jss::ledger_index] = jss::validated;
3086 jvParams[jss::vault_id] = strHex(keylet.key);
3087 jvParams[jss::owner] = owner.human();
3088 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3089 BEAST_EXPECT(
3090 jv[jss::result][jss::error].asString() == "malformedRequest");
3091 }
3092
3093 {
3094 testcase(
3095 "RPC vault_info json invalid combination owner seq "
3096 "vault_id");
3097 Json::Value jvParams;
3098 jvParams[jss::ledger_index] = jss::validated;
3099 jvParams[jss::vault_id] = strHex(keylet.key);
3100 jvParams[jss::seq] = sequence;
3101 jvParams[jss::owner] = owner.human();
3102 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3103 BEAST_EXPECT(
3104 jv[jss::result][jss::error].asString() == "malformedRequest");
3105 }
3106
3107 {
3108 testcase("RPC vault_info json no input");
3109 Json::Value jvParams;
3110 jvParams[jss::ledger_index] = jss::validated;
3111 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3112 BEAST_EXPECT(
3113 jv[jss::result][jss::error].asString() == "malformedRequest");
3114 }
3115
3116 {
3117 testcase("RPC vault_info command line invalid index");
3118 Json::Value jv = env.rpc("vault_info", "foobar", "validated");
3119 BEAST_EXPECT(jv[jss::error].asString() == "invalidParams");
3120 }
3121
3122 {
3123 testcase("RPC vault_info command line invalid index");
3124 Json::Value jv = env.rpc("vault_info", "0", "validated");
3125 BEAST_EXPECT(
3126 jv[jss::result][jss::error].asString() == "malformedRequest");
3127 }
3128
3129 {
3130 testcase("RPC vault_info command line invalid index");
3131 Json::Value jv =
3132 env.rpc("vault_info", strHex(uint256(42)), "validated");
3133 BEAST_EXPECT(
3134 jv[jss::result][jss::error].asString() == "entryNotFound");
3135 }
3136
3137 {
3138 testcase("RPC vault_info command line invalid ledger");
3139 Json::Value jv = env.rpc("vault_info", strHex(keylet.key), "0");
3140 BEAST_EXPECT(
3141 jv[jss::result][jss::error].asString() == "lgrNotFound");
3142 }
3143 }
3144
3145public:
3146 void
3147 run() override
3148 {
3149 testSequences();
3150 testPreflight();
3154 testWithMPT();
3155 testWithIOU();
3160 testRPC();
3161 }
3162};
3163
3164BEAST_DEFINE_TESTSUITE_PRIO(Vault, tx, ripple, 1);
3165
3166} // namespace ripple
Represents a JSON value.
Definition: json_value.h:150
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
constexpr TIss const & get() const
A currency issued by an account.
Definition: Issue.h:36
constexpr MPTID const & getMptID() const
Definition: MPTIssue.h:46
Identifies fields.
Definition: SField.h:143
void setIssuer(AccountID const &uIssuer)
Definition: STAmount.h:588
Issue const & issue() const
Definition: STAmount.h:496
void testFailedPseudoAccount()
ripple::test::jtx::PrettyAsset PrettyAsset
Definition: Vault_test.cpp:58
void testNonTransferableShares()
static auto constexpr negativeAmount
Definition: Vault_test.cpp:61
void run() override
Runs the suite.
void testWithDomainCheckXRP()
ripple::test::jtx::PrettyAmount PrettyAmount
Definition: Vault_test.cpp:59
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:86
@ nullValue
'null' value
Definition: json_value.h:39
int Int
Definition: json_forwards.h:26
unsigned int UInt
Definition: json_forwards.h:27
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition: Indexes.cpp:533
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:237
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition: Indexes.cpp:519
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition: Indexes.cpp:557
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:177
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition: Indexes.cpp:189
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition: AccountID.h:49
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:118
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:114
constexpr std::uint32_t asfGlobalFreeze
Definition: TxFlags.h:83
constexpr std::uint32_t asfDepositAuth
Definition: TxFlags.h:85
@ telINSUF_FEE_P
Definition: TER.h:57
constexpr std::uint32_t const tfMPTCanTransfer
Definition: TxFlags.h:154
constexpr std::uint32_t asfRequireDest
Definition: TxFlags.h:77
base_uint< 256 > uint256
Definition: base_uint.h:558
base_uint< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition: UintTypes.h:64
LedgerSpecificFlags
@ lsfMPTCanTransfer
@ lsfMPTCanTrade
@ lsfMPTCanEscrow
@ lsfMPTCanClawback
constexpr std::uint32_t const tfVaultPrivate
Definition: TxFlags.h:240
Json::Value to_json(Asset const &asset)
Definition: Asset.h:123
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
constexpr std::uint32_t const tfMPTUnauthorize
Definition: TxFlags.h:160
constexpr std::uint32_t tfSetfAuth
Definition: TxFlags.h:114
constexpr std::uint32_t asfDefaultRipple
Definition: TxFlags.h:84
constexpr std::uint32_t tfClearFreeze
Definition: TxFlags.h:118
@ tecNO_ENTRY
Definition: TER.h:306
@ tecLIMIT_EXCEEDED
Definition: TER.h:361
@ tecOBJECT_NOT_FOUND
Definition: TER.h:326
@ tecFROZEN
Definition: TER.h:303
@ tecINSUFFICIENT_FUNDS
Definition: TER.h:325
@ tecNO_PERMISSION
Definition: TER.h:305
@ tecDST_TAG_NEEDED
Definition: TER.h:309
@ tecHAS_OBLIGATIONS
Definition: TER.h:317
@ tecWRONG_ASSET
Definition: TER.h:360
@ tecNO_LINE
Definition: TER.h:301
@ tecINSUFFICIENT_RESERVE
Definition: TER.h:307
@ tecEXPIRED
Definition: TER.h:314
@ tecNO_AUTH
Definition: TER.h:300
@ tecLOCKED
Definition: TER.h:358
constexpr std::uint32_t const tfMPTLock
Definition: TxFlags.h:164
@ tesSUCCESS
Definition: TER.h:244
constexpr std::uint32_t const tfVaultShareNonTransferable
Definition: TxFlags.h:242
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition: View.cpp:1056
constexpr std::uint32_t tfClearDeepFreeze
Definition: TxFlags.h:120
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
std::uint8_t constexpr vaultStrategyFirstComeFirstServe
Vault withdrawal policies.
Definition: Protocol.h:123
constexpr std::uint32_t asfAllowTrustLineClawback
Definition: TxFlags.h:94
constexpr std::uint32_t asfRequireAuth
Definition: TxFlags.h:78
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
Definition: Indexes.cpp:163
@ terADDRESS_COLLISION
Definition: TER.h:228
@ terNO_ACCOUNT
Definition: TER.h:217
@ terNO_RIPPLE
Definition: TER.h:224
constexpr std::uint32_t const tfMPTRequireAuth
Definition: TxFlags.h:151
constexpr std::uint32_t tfSetFreeze
Definition: TxFlags.h:117
constexpr std::uint32_t const tfMPTCanLock
Definition: TxFlags.h:150
constexpr std::uint32_t const tfMPTCanClawback
Definition: TxFlags.h:155
@ temBAD_AMOUNT
Definition: TER.h:89
@ temBAD_FEE
Definition: TER.h:92
@ temMALFORMED
Definition: TER.h:87
@ temINVALID_FLAG
Definition: TER.h:111
@ temDISABLED
Definition: TER.h:114
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:39
uint256 key
Definition: Keylet.h:40
Represents an XRP or IOU quantity This customizes the string conversion and supports XRP conversions ...