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