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