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, testable_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 testable_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 = testable_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 = (testable_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, testable_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{*this, testable_amendments() | featureSingleAssetVault};
1096 Account issuer{"issuer"};
1097 Account owner{"owner"};
1098 env.fund(XRP(1000), issuer, owner);
1099 env.close();
1100 env(fset(issuer, asfGlobalFreeze));
1101 env.close();
1102
1103 Vault vault{env};
1104 Asset asset = issuer["IOU"];
1105 auto [tx, keylet] =
1106 vault.create({.owner = owner, .asset = asset});
1107
1108 env(tx, ter(tecFROZEN));
1109 env.close();
1110 }
1111
1112 {
1113 testcase("IOU fail create no ripling");
1114 Env env{*this, testable_amendments() | featureSingleAssetVault};
1115 Account issuer{"issuer"};
1116 Account owner{"owner"};
1117 env.fund(XRP(1000), issuer, owner);
1118 env.close();
1119 env(fclear(issuer, asfDefaultRipple));
1120 env.close();
1121
1122 Vault vault{env};
1123 Asset asset = issuer["IOU"];
1124 auto [tx, keylet] =
1125 vault.create({.owner = owner, .asset = asset});
1126 env(tx, ter(terNO_RIPPLE));
1127 env.close();
1128 }
1129
1130 {
1131 testcase("IOU no issuer");
1132 Env env{*this, testable_amendments() | featureSingleAssetVault};
1133 Account issuer{"issuer"};
1134 Account owner{"owner"};
1135 env.fund(XRP(1000), owner);
1136 env.close();
1137
1138 Vault vault{env};
1139 Asset asset = issuer["IOU"];
1140 {
1141 auto [tx, keylet] =
1142 vault.create({.owner = owner, .asset = asset});
1143 env(tx, ter(terNO_ACCOUNT));
1144 env.close();
1145 }
1146 }
1147 }
1148
1149 {
1150 testcase("IOU fail create vault for AMM LPToken");
1151 Env env{*this, testable_amendments() | featureSingleAssetVault};
1152 Account const gw("gateway");
1153 Account const alice("alice");
1154 Account const carol("carol");
1155 IOU const USD = gw["USD"];
1156
1157 auto const [asset1, asset2] =
1158 std::pair<STAmount, STAmount>(XRP(10000), USD(10000));
1159 auto tofund = [&](STAmount const& a) -> STAmount {
1160 if (a.native())
1161 {
1162 auto const defXRP = XRP(30000);
1163 if (a <= defXRP)
1164 return defXRP;
1165 return a + XRP(1000);
1166 }
1167 auto const defIOU = STAmount{a.issue(), 30000};
1168 if (a <= defIOU)
1169 return defIOU;
1170 return a + STAmount{a.issue(), 1000};
1171 };
1172 auto const toFund1 = tofund(asset1);
1173 auto const toFund2 = tofund(asset2);
1174 BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2);
1175
1176 if (!asset1.native() && !asset2.native())
1177 fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All);
1178 else if (asset1.native())
1179 fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All);
1180 else if (asset2.native())
1181 fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All);
1182
1183 AMM ammAlice(
1184 env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0});
1185
1186 Account const owner{"owner"};
1187 env.fund(XRP(1000000), owner);
1188
1189 Vault vault{env};
1190 auto [tx, k] =
1191 vault.create({.owner = owner, .asset = ammAlice.lptIssue()});
1192 env(tx, ter{tecWRONG_ASSET});
1193 env.close();
1194 }
1195 }
1196
1197 void
1199 {
1200 using namespace test::jtx;
1201
1202 Env env{*this, testable_amendments() | featureSingleAssetVault};
1203 Account issuer{"issuer"};
1204 Account owner{"owner"};
1205 Account depositor{"depositor"};
1206 env.fund(XRP(1000), issuer, owner, depositor);
1207 env.close();
1208 Vault vault{env};
1209
1210 MPTTester mptt{env, issuer, mptInitNoFund};
1211
1212 // Locked because that is the default flag.
1213 mptt.create();
1214 Asset asset = mptt.issuanceID();
1215 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1216 env(tx, ter(tecNO_AUTH));
1217 }
1218
1219 void
1221 {
1222 using namespace test::jtx;
1223
1224 Env env{*this, testable_amendments() | featureSingleAssetVault};
1225 Account issuer{"issuer"};
1226 Account owner{"owner"};
1227 Account depositor{"depositor"};
1228 env.fund(XRP(1000), issuer, owner, depositor);
1229 env.close();
1230
1231 Vault vault{env};
1232 PrettyAsset asset = issuer["IOU"];
1233 env.trust(asset(1000), owner);
1234 env(pay(issuer, owner, asset(100)));
1235 env.trust(asset(1000), depositor);
1236 env(pay(issuer, depositor, asset(100)));
1237 env.close();
1238
1239 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1240 tx[sfFlags] = tfVaultShareNonTransferable;
1241 env(tx);
1242 env.close();
1243
1244 {
1245 testcase("nontransferable deposits");
1246 auto tx1 = vault.deposit(
1247 {.depositor = depositor,
1248 .id = keylet.key,
1249 .amount = asset(40)});
1250 env(tx1);
1251
1252 auto tx2 = vault.deposit(
1253 {.depositor = owner, .id = keylet.key, .amount = asset(60)});
1254 env(tx2);
1255 env.close();
1256 }
1257
1258 auto const vaultAccount = //
1259 [&env, key = keylet.key, this]() -> AccountID {
1260 auto jvVault = env.rpc("vault_info", strHex(key));
1261
1262 BEAST_EXPECT(
1263 jvVault[jss::result][jss::vault][sfAssetsTotal] == "100");
1264 BEAST_EXPECT(
1265 jvVault[jss::result][jss::vault][jss::shares]
1266 [sfOutstandingAmount] == "100");
1267
1268 // Vault pseudo-account
1269 return parseBase58<AccountID>(
1270 jvVault[jss::result][jss::vault][jss::Account]
1271 .asString())
1272 .value();
1273 }();
1274
1275 auto const MptID = makeMptID(1, vaultAccount);
1276 Asset shares = MptID;
1277
1278 {
1279 testcase("nontransferable shares cannot be moved");
1280 env(pay(owner, depositor, shares(10)), ter{tecNO_AUTH});
1281 env(pay(depositor, owner, shares(10)), ter{tecNO_AUTH});
1282 }
1283
1284 {
1285 testcase("nontransferable shares can be used to withdraw");
1286 auto tx1 = vault.withdraw(
1287 {.depositor = depositor,
1288 .id = keylet.key,
1289 .amount = asset(20)});
1290 env(tx1);
1291
1292 auto tx2 = vault.withdraw(
1293 {.depositor = owner, .id = keylet.key, .amount = asset(30)});
1294 env(tx2);
1295 env.close();
1296 }
1297
1298 {
1299 testcase("nontransferable shares balance check");
1300 auto jvVault = env.rpc("vault_info", strHex(keylet.key));
1301 BEAST_EXPECT(
1302 jvVault[jss::result][jss::vault][sfAssetsTotal] == "50");
1303 BEAST_EXPECT(
1304 jvVault[jss::result][jss::vault][jss::shares]
1305 [sfOutstandingAmount] == "50");
1306 }
1307
1308 {
1309 testcase("nontransferable shares withdraw rest");
1310 auto tx1 = vault.withdraw(
1311 {.depositor = depositor,
1312 .id = keylet.key,
1313 .amount = asset(20)});
1314 env(tx1);
1315
1316 auto tx2 = vault.withdraw(
1317 {.depositor = owner, .id = keylet.key, .amount = asset(30)});
1318 env(tx2);
1319 env.close();
1320 }
1321
1322 {
1323 testcase("nontransferable shares delete empty vault");
1324 auto tx = vault.del({.owner = owner, .id = keylet.key});
1325 env(tx);
1326 BEAST_EXPECT(!env.le(keylet));
1327 }
1328 }
1329
1330 void
1332 {
1333 using namespace test::jtx;
1334
1335 struct CaseArgs
1336 {
1337 bool enableClawback = true;
1338 };
1339
1340 auto testCase = [this](
1341 std::function<void(
1342 Env & env,
1343 Account const& issuer,
1344 Account const& owner,
1345 Account const& depositor,
1346 Asset const& asset,
1347 Vault& vault,
1348 MPTTester& mptt)> test,
1349 CaseArgs args = {}) {
1350 Env env{*this, testable_amendments() | featureSingleAssetVault};
1351 Account issuer{"issuer"};
1352 Account owner{"owner"};
1353 Account depositor{"depositor"};
1354 env.fund(XRP(1000), issuer, owner, depositor);
1355 env.close();
1356 Vault vault{env};
1357
1358 MPTTester mptt{env, issuer, mptInitNoFund};
1359 mptt.create(
1360 {.flags = tfMPTCanTransfer | tfMPTCanLock |
1361 (args.enableClawback ? lsfMPTCanClawback
1362 : LedgerSpecificFlags(0)) |
1364 PrettyAsset asset = mptt.issuanceID();
1365 mptt.authorize({.account = owner});
1366 mptt.authorize({.account = issuer, .holder = owner});
1367 mptt.authorize({.account = depositor});
1368 mptt.authorize({.account = issuer, .holder = depositor});
1369 env(pay(issuer, depositor, asset(1000)));
1370 env.close();
1371
1372 test(env, issuer, owner, depositor, asset, vault, mptt);
1373 };
1374
1375 testCase([this](
1376 Env& env,
1377 Account const& issuer,
1378 Account const& owner,
1379 Account const& depositor,
1380 PrettyAsset const& asset,
1381 Vault& vault,
1382 MPTTester& mptt) {
1383 testcase("MPT nothing to clawback from");
1384 auto tx = vault.clawback(
1385 {.issuer = issuer,
1386 .id = keylet::skip().key,
1387 .holder = depositor,
1388 .amount = asset(10)});
1389 env(tx, ter(tecNO_ENTRY));
1390 });
1391
1392 testCase([this](
1393 Env& env,
1394 Account const& issuer,
1395 Account const& owner,
1396 Account const& depositor,
1397 Asset const& asset,
1398 Vault& vault,
1399 MPTTester& mptt) {
1400 testcase("MPT global lock blocks create");
1401 mptt.set({.account = issuer, .flags = tfMPTLock});
1402 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1403 env(tx, ter(tecLOCKED));
1404 });
1405
1406 testCase([this](
1407 Env& env,
1408 Account const& issuer,
1409 Account const& owner,
1410 Account const& depositor,
1411 Asset const& asset,
1412 Vault& vault,
1413 MPTTester& mptt) {
1414 testcase("MPT global lock blocks deposit");
1415 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1416 env(tx);
1417 env.close();
1418
1419 mptt.set({.account = issuer, .flags = tfMPTLock});
1420 env.close();
1421
1422 tx = vault.deposit(
1423 {.depositor = depositor,
1424 .id = keylet.key,
1425 .amount = asset(100)});
1426 env(tx, ter{tecLOCKED});
1427 env.close();
1428
1429 // Can delete empty vault, even if global lock
1430 tx = vault.del({.owner = owner, .id = keylet.key});
1431 env(tx);
1432 });
1433
1434 testCase([this](
1435 Env& env,
1436 Account const& issuer,
1437 Account const& owner,
1438 Account const& depositor,
1439 Asset const& asset,
1440 Vault& vault,
1441 MPTTester& mptt) {
1442 testcase("MPT global lock blocks withdrawal");
1443 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1444 env(tx);
1445 env.close();
1446 tx = vault.deposit(
1447 {.depositor = depositor,
1448 .id = keylet.key,
1449 .amount = asset(100)});
1450 env(tx);
1451 env.close();
1452
1453 // Check that the OutstandingAmount field of MPTIssuance
1454 // accounts for the issued shares.
1455 auto v = env.le(keylet);
1456 BEAST_EXPECT(v);
1457 MPTID share = (*v)[sfShareMPTID];
1458 auto issuance = env.le(keylet::mptIssuance(share));
1459 BEAST_EXPECT(issuance);
1460 Number outstandingShares = issuance->at(sfOutstandingAmount);
1461 BEAST_EXPECT(outstandingShares == 100);
1462
1463 mptt.set({.account = issuer, .flags = tfMPTLock});
1464 env.close();
1465
1466 tx = vault.withdraw(
1467 {.depositor = depositor,
1468 .id = keylet.key,
1469 .amount = asset(100)});
1470 env(tx, ter(tecLOCKED));
1471
1472 tx[sfDestination] = issuer.human();
1473 env(tx, ter(tecLOCKED));
1474
1475 // Clawback is still permitted, even with global lock
1476 tx = vault.clawback(
1477 {.issuer = issuer,
1478 .id = keylet.key,
1479 .holder = depositor,
1480 .amount = asset(0)});
1481 env(tx);
1482 env.close();
1483
1484 // Can delete empty vault, even if global lock
1485 tx = vault.del({.owner = owner, .id = keylet.key});
1486 env(tx);
1487 });
1488
1489 testCase([this](
1490 Env& env,
1491 Account const& issuer,
1492 Account const& owner,
1493 Account const& depositor,
1494 PrettyAsset const& asset,
1495 Vault& vault,
1496 MPTTester& mptt) {
1497 testcase("MPT only issuer can clawback");
1498
1499 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1500 env(tx);
1501 env.close();
1502
1503 tx = vault.deposit(
1504 {.depositor = depositor,
1505 .id = keylet.key,
1506 .amount = asset(100)});
1507 env(tx);
1508 env.close();
1509
1510 {
1511 auto tx = vault.clawback(
1512 {.issuer = owner, .id = keylet.key, .holder = depositor});
1513 env(tx, ter(tecNO_PERMISSION));
1514 }
1515 });
1516
1517 testCase([this](
1518 Env& env,
1519 Account const& issuer,
1520 Account const& owner,
1521 Account const& depositor,
1522 PrettyAsset const& asset,
1523 Vault& vault,
1524 MPTTester& mptt) {
1525 testcase("MPT issuance deleted");
1526
1527 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1528 env(tx);
1529 env.close();
1530
1531 tx = vault.deposit(
1532 {.depositor = depositor,
1533 .id = keylet.key,
1534 .amount = asset(1000)});
1535 env(tx);
1536 env.close();
1537
1538 {
1539 auto tx = vault.clawback(
1540 {.issuer = issuer,
1541 .id = keylet.key,
1542 .holder = depositor,
1543 .amount = asset(0)});
1544 env(tx);
1545 }
1546
1547 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
1548 env.close();
1549
1550 {
1551 auto [tx, keylet] =
1552 vault.create({.owner = depositor, .asset = asset});
1553 env(tx, ter{tecOBJECT_NOT_FOUND});
1554 }
1555
1556 {
1557 auto tx = vault.deposit(
1558 {.depositor = depositor,
1559 .id = keylet.key,
1560 .amount = asset(10)});
1561 env(tx, ter{tecOBJECT_NOT_FOUND});
1562 }
1563
1564 {
1565 auto tx = vault.withdraw(
1566 {.depositor = depositor,
1567 .id = keylet.key,
1568 .amount = asset(10)});
1569 env(tx, ter{tecOBJECT_NOT_FOUND});
1570 }
1571
1572 {
1573 auto tx = vault.clawback(
1574 {.issuer = issuer,
1575 .id = keylet.key,
1576 .holder = depositor,
1577 .amount = asset(0)});
1578 env(tx, ter{tecOBJECT_NOT_FOUND});
1579 }
1580
1581 env(vault.del({.owner = owner, .id = keylet.key}));
1582 });
1583
1584 testCase(
1585 [this](
1586 Env& env,
1587 Account const& issuer,
1588 Account const& owner,
1589 Account const& depositor,
1590 PrettyAsset const& asset,
1591 Vault& vault,
1592 MPTTester& mptt) {
1593 testcase("MPT clawback disabled");
1594
1595 auto [tx, keylet] =
1596 vault.create({.owner = owner, .asset = asset});
1597 env(tx);
1598 env.close();
1599
1600 tx = vault.deposit(
1601 {.depositor = depositor,
1602 .id = keylet.key,
1603 .amount = asset(1000)});
1604 env(tx);
1605 env.close();
1606
1607 {
1608 auto tx = vault.clawback(
1609 {.issuer = issuer,
1610 .id = keylet.key,
1611 .holder = depositor,
1612 .amount = asset(0)});
1613 env(tx, ter{tecNO_PERMISSION});
1614 }
1615 },
1616 {.enableClawback = false});
1617
1618 testCase([this](
1619 Env& env,
1620 Account const& issuer,
1621 Account const& owner,
1622 Account const& depositor,
1623 Asset const& asset,
1624 Vault& vault,
1625 MPTTester& mptt) {
1626 testcase("MPT un-authorization");
1627 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1628 env(tx);
1629 env.close();
1630 tx = vault.deposit(
1631 {.depositor = depositor,
1632 .id = keylet.key,
1633 .amount = asset(1000)});
1634 env(tx);
1635 env.close();
1636
1637 mptt.authorize(
1638 {.account = issuer,
1639 .holder = depositor,
1640 .flags = tfMPTUnauthorize});
1641 env.close();
1642
1643 {
1644 auto tx = vault.withdraw(
1645 {.depositor = depositor,
1646 .id = keylet.key,
1647 .amount = asset(100)});
1648 env(tx, ter(tecNO_AUTH));
1649
1650 // Withdrawal to other (authorized) accounts works
1651 tx[sfDestination] = issuer.human();
1652 env(tx);
1653 tx[sfDestination] = owner.human();
1654 env(tx);
1655 env.close();
1656 }
1657
1658 {
1659 // Cannot deposit some more
1660 auto tx = vault.deposit(
1661 {.depositor = depositor,
1662 .id = keylet.key,
1663 .amount = asset(100)});
1664 env(tx, ter(tecNO_AUTH));
1665 }
1666
1667 // Clawback works
1668 tx = vault.clawback(
1669 {.issuer = issuer,
1670 .id = keylet.key,
1671 .holder = depositor,
1672 .amount = asset(800)});
1673 env(tx);
1674
1675 env(vault.del({.owner = owner, .id = keylet.key}));
1676 });
1677
1678 testCase([this](
1679 Env& env,
1680 Account const& issuer,
1681 Account const& owner,
1682 Account const& depositor,
1683 Asset const& asset,
1684 Vault& vault,
1685 MPTTester& mptt) {
1686 testcase("MPT lock of vault pseudo-account");
1687 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1688 env(tx);
1689 env.close();
1690
1691 auto const vaultAccount =
1692 [&env, keylet = keylet, this]() -> AccountID {
1693 auto const vault = env.le(keylet);
1694 BEAST_EXPECT(vault != nullptr);
1695 return vault->at(sfAccount);
1696 }();
1697
1698 tx = vault.deposit(
1699 {.depositor = depositor,
1700 .id = keylet.key,
1701 .amount = asset(100)});
1702 env(tx);
1703 env.close();
1704
1705 tx = [&]() {
1706 Json::Value jv;
1707 jv[jss::Account] = issuer.human();
1708 jv[sfMPTokenIssuanceID] =
1709 to_string(asset.get<MPTIssue>().getMptID());
1710 jv[jss::Holder] = toBase58(vaultAccount);
1711 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
1712 jv[jss::Flags] = tfMPTLock;
1713 return jv;
1714 }();
1715 env(tx);
1716 env.close();
1717
1718 tx = vault.deposit(
1719 {.depositor = depositor,
1720 .id = keylet.key,
1721 .amount = asset(100)});
1722 env(tx, ter(tecLOCKED));
1723
1724 tx = vault.withdraw(
1725 {.depositor = depositor,
1726 .id = keylet.key,
1727 .amount = asset(100)});
1728 env(tx, ter(tecLOCKED));
1729
1730 // Clawback works, even when locked
1731 tx = vault.clawback(
1732 {.issuer = issuer,
1733 .id = keylet.key,
1734 .holder = depositor,
1735 .amount = asset(100)});
1736 env(tx);
1737
1738 // Can delete an empty vault even when asset is locked.
1739 tx = vault.del({.owner = owner, .id = keylet.key});
1740 env(tx);
1741 });
1742
1743 {
1744 testcase("MPT shares to a vault");
1745
1746 Env env{*this, testable_amendments() | featureSingleAssetVault};
1747 Account owner{"owner"};
1748 Account issuer{"issuer"};
1749 env.fund(XRP(1000000), owner, issuer);
1750 env.close();
1751 Vault vault{env};
1752
1753 MPTTester mptt{env, issuer, mptInitNoFund};
1754 mptt.create(
1757 mptt.authorize({.account = owner});
1758 mptt.authorize({.account = issuer, .holder = owner});
1759 PrettyAsset asset = mptt.issuanceID();
1760 env(pay(issuer, owner, asset(100)));
1761 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
1762 env(tx1);
1763 env.close();
1764
1765 auto const shares = [&env, keylet = k1, this]() -> Asset {
1766 auto const vault = env.le(keylet);
1767 BEAST_EXPECT(vault != nullptr);
1768 return MPTIssue(vault->at(sfShareMPTID));
1769 }();
1770
1771 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
1772 env(tx2, ter{tecWRONG_ASSET});
1773 env.close();
1774 }
1775 }
1776
1777 void
1779 {
1780 using namespace test::jtx;
1781
1782 auto testCase =
1783 [&, this](
1784 std::function<void(
1785 Env & env,
1786 Account const& owner,
1787 Account const& issuer,
1788 Account const& charlie,
1789 std::function<AccountID(ripple::Keylet)> vaultAccount,
1790 Vault& vault,
1791 PrettyAsset const& asset,
1792 std::function<MPTID(ripple::Keylet)> issuanceId,
1794 test) {
1795 Env env{*this, testable_amendments() | featureSingleAssetVault};
1796 Account const owner{"owner"};
1797 Account const issuer{"issuer"};
1798 Account const charlie{"charlie"};
1799 Vault vault{env};
1800 env.fund(XRP(1000), issuer, owner, charlie);
1801 env(fset(issuer, asfAllowTrustLineClawback));
1802 env.close();
1803
1804 PrettyAsset const asset = issuer["IOU"];
1805 env.trust(asset(1000), owner);
1806 env(pay(issuer, owner, asset(200)));
1807 env(rate(issuer, 1.25));
1808 env.close();
1809
1810 auto const [tx, keylet] =
1811 vault.create({.owner = owner, .asset = asset});
1812 env(tx);
1813 env.close();
1814
1815 auto const vaultAccount =
1816 [&env](ripple::Keylet keylet) -> AccountID {
1817 return env.le(keylet)->at(sfAccount);
1818 };
1819 auto const issuanceId = [&env](ripple::Keylet keylet) -> MPTID {
1820 return env.le(keylet)->at(sfShareMPTID);
1821 };
1822 auto const vaultBalance = //
1823 [&env, &vaultAccount, issue = asset.raw().get<Issue>()](
1824 ripple::Keylet keylet) -> PrettyAmount {
1825 auto const account = vaultAccount(keylet);
1826 auto const sle = env.le(keylet::line(account, issue));
1827 if (sle == nullptr)
1828 return {
1829 STAmount(issue, 0),
1830 env.lookup(issue.account).name()};
1831 auto amount = sle->getFieldAmount(sfBalance);
1832 amount.setIssuer(issue.account);
1833 if (account > issue.account)
1834 amount.negate();
1835 return {amount, env.lookup(issue.account).name()};
1836 };
1837
1838 test(
1839 env,
1840 owner,
1841 issuer,
1842 charlie,
1843 vaultAccount,
1844 vault,
1845 asset,
1846 issuanceId,
1847 vaultBalance);
1848 };
1849
1850 testCase([&, this](
1851 Env& env,
1852 Account const& owner,
1853 Account const& issuer,
1854 Account const&,
1855 auto vaultAccount,
1856 Vault& vault,
1857 PrettyAsset const& asset,
1858 auto&&...) {
1859 testcase("IOU cannot use different asset");
1860 PrettyAsset const foo = issuer["FOO"];
1861
1862 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1863 env(tx);
1864 env.close();
1865
1866 {
1867 // Cannot create new trustline to a vault
1868 auto tx = [&, account = vaultAccount(keylet)]() {
1869 Json::Value jv;
1870 jv[jss::Account] = issuer.human();
1871 {
1872 auto& ja = jv[jss::LimitAmount] =
1873 foo(0).value().getJson(JsonOptions::none);
1874 ja[jss::issuer] = toBase58(account);
1875 }
1876 jv[jss::TransactionType] = jss::TrustSet;
1877 jv[jss::Flags] = tfSetFreeze;
1878 return jv;
1879 }();
1880 env(tx, ter{tecNO_PERMISSION});
1881 env.close();
1882 }
1883
1884 {
1885 auto tx = vault.deposit(
1886 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
1887 env(tx, ter{tecWRONG_ASSET});
1888 env.close();
1889 }
1890
1891 {
1892 auto tx = vault.withdraw(
1893 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
1894 env(tx, ter{tecWRONG_ASSET});
1895 env.close();
1896 }
1897
1898 env(vault.del({.owner = owner, .id = keylet.key}));
1899 env.close();
1900 });
1901
1902 testCase([&, this](
1903 Env& env,
1904 Account const& owner,
1905 Account const& issuer,
1906 Account const& charlie,
1907 auto vaultAccount,
1908 Vault& vault,
1909 PrettyAsset const& asset,
1910 auto issuanceId,
1911 auto) {
1912 testcase("IOU frozen trust line to vault account");
1913
1914 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1915 env(tx);
1916 env.close();
1917
1918 env(vault.deposit(
1919 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
1920 env.close();
1921
1922 Asset const share = Asset(issuanceId(keylet));
1923
1924 // Freeze the trustline to the vault
1925 auto trustSet = [&, account = vaultAccount(keylet)]() {
1926 Json::Value jv;
1927 jv[jss::Account] = issuer.human();
1928 {
1929 auto& ja = jv[jss::LimitAmount] =
1930 asset(0).value().getJson(JsonOptions::none);
1931 ja[jss::issuer] = toBase58(account);
1932 }
1933 jv[jss::TransactionType] = jss::TrustSet;
1934 jv[jss::Flags] = tfSetFreeze;
1935 return jv;
1936 }();
1937 env(trustSet);
1938 env.close();
1939
1940 {
1941 // Note, the "frozen" state of the trust line to vault account
1942 // is reported as "locked" state of the vault shares, because
1943 // this state is attached to shares by means of the transitive
1944 // isFrozen.
1945 auto tx = vault.deposit(
1946 {.depositor = owner,
1947 .id = keylet.key,
1948 .amount = asset(80)});
1949 env(tx, ter{tecLOCKED});
1950 }
1951
1952 {
1953 auto tx = vault.withdraw(
1954 {.depositor = owner,
1955 .id = keylet.key,
1956 .amount = asset(100)});
1957 env(tx, ter{tecLOCKED});
1958
1959 // also when trying to withdraw to a 3rd party
1960 tx[sfDestination] = charlie.human();
1961 env(tx, ter{tecLOCKED});
1962 env.close();
1963 }
1964
1965 {
1966 // Clawback works, even when locked
1967 auto tx = vault.clawback(
1968 {.issuer = issuer,
1969 .id = keylet.key,
1970 .holder = owner,
1971 .amount = asset(50)});
1972 env(tx);
1973 env.close();
1974 }
1975
1976 // Clear the frozen state
1977 trustSet[jss::Flags] = tfClearFreeze;
1978 env(trustSet);
1979 env.close();
1980
1981 env(vault.withdraw(
1982 {.depositor = owner, .id = keylet.key, .amount = share(50)}));
1983
1984 env(vault.del({.owner = owner, .id = keylet.key}));
1985 env.close();
1986 });
1987
1988 testCase([&, this](
1989 Env& env,
1990 Account const& owner,
1991 Account const& issuer,
1992 Account const& charlie,
1993 auto,
1994 Vault& vault,
1995 PrettyAsset const& asset,
1996 auto issuanceId,
1997 auto vaultBalance) {
1998 testcase("IOU transfer fees not applied");
1999
2000 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2001 env(tx);
2002 env.close();
2003
2004 env(vault.deposit(
2005 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2006 env.close();
2007
2008 auto const issue = asset.raw().get<Issue>();
2009 Asset const share = Asset(issuanceId(keylet));
2010
2011 // transfer fees ignored on deposit
2012 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2013 BEAST_EXPECT(vaultBalance(keylet) == asset(100));
2014
2015 {
2016 auto tx = vault.clawback(
2017 {.issuer = issuer,
2018 .id = keylet.key,
2019 .holder = owner,
2020 .amount = asset(50)});
2021 env(tx);
2022 env.close();
2023 }
2024
2025 // transfer fees ignored on clawback
2026 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2027 BEAST_EXPECT(vaultBalance(keylet) == asset(50));
2028
2029 env(vault.withdraw(
2030 {.depositor = owner, .id = keylet.key, .amount = share(20)}));
2031
2032 // transfer fees ignored on withdraw
2033 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2034 BEAST_EXPECT(vaultBalance(keylet) == asset(30));
2035
2036 {
2037 auto tx = vault.withdraw(
2038 {.depositor = owner,
2039 .id = keylet.key,
2040 .amount = share(30)});
2041 tx[sfDestination] = charlie.human();
2042 env(tx);
2043 }
2044
2045 // transfer fees ignored on withdraw to 3rd party
2046 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2047 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2048 BEAST_EXPECT(vaultBalance(keylet) == asset(0));
2049
2050 env(vault.del({.owner = owner, .id = keylet.key}));
2051 env.close();
2052 });
2053
2054 testCase([&, this](
2055 Env& env,
2056 Account const& owner,
2057 Account const& issuer,
2058 Account const& charlie,
2059 auto,
2060 Vault& vault,
2061 PrettyAsset const& asset,
2062 auto&&...) {
2063 testcase("IOU frozen trust line to depositor");
2064
2065 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2066 env(tx);
2067 env.close();
2068
2069 env(vault.deposit(
2070 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2071 env.close();
2072
2073 // Withdraw to 3rd party works
2074 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
2075 auto tx = vault.withdraw(
2076 {.depositor = owner,
2077 .id = keylet.key,
2078 .amount = asset(10)});
2079 tx[sfDestination] = charlie.human();
2080 return tx;
2081 }(keylet);
2082 env(withdrawToCharlie);
2083
2084 // Freeze the owner
2085 env(trust(issuer, asset(0), owner, tfSetFreeze));
2086 env.close();
2087
2088 // Cannot withdraw
2089 auto const withdraw = vault.withdraw(
2090 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
2091 env(withdraw, ter{tecFROZEN});
2092
2093 // Cannot withdraw to 3rd party
2094 env(withdrawToCharlie, ter{tecLOCKED});
2095 env.close();
2096
2097 {
2098 // Cannot deposit some more
2099 auto tx = vault.deposit(
2100 {.depositor = owner,
2101 .id = keylet.key,
2102 .amount = asset(10)});
2103 env(tx, ter{tecFROZEN});
2104 }
2105
2106 {
2107 // Clawback still works
2108 auto tx = vault.clawback(
2109 {.issuer = issuer,
2110 .id = keylet.key,
2111 .holder = owner,
2112 .amount = asset(0)});
2113 env(tx);
2114 env.close();
2115 }
2116
2117 env(vault.del({.owner = owner, .id = keylet.key}));
2118 env.close();
2119 });
2120
2121 testCase([&, this](
2122 Env& env,
2123 Account const& owner,
2124 Account const& issuer,
2125 Account const& charlie,
2126 auto,
2127 Vault& vault,
2128 PrettyAsset const& asset,
2129 auto&&...) {
2130 testcase("IOU frozen trust line to 3rd party");
2131
2132 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2133 env(tx);
2134 env.close();
2135
2136 env(vault.deposit(
2137 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2138 env.close();
2139
2140 // Withdraw to 3rd party works
2141 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
2142 auto tx = vault.withdraw(
2143 {.depositor = owner,
2144 .id = keylet.key,
2145 .amount = asset(10)});
2146 tx[sfDestination] = charlie.human();
2147 return tx;
2148 }(keylet);
2149 env(withdrawToCharlie);
2150
2151 // Freeze the 3rd party
2152 env(trust(issuer, asset(0), charlie, tfSetFreeze));
2153 env.close();
2154
2155 // Can withdraw
2156 auto const withdraw = vault.withdraw(
2157 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
2158 env(withdraw);
2159 env.close();
2160
2161 // Cannot withdraw to 3rd party
2162 env(withdrawToCharlie, ter{tecFROZEN});
2163 env.close();
2164
2165 env(vault.clawback(
2166 {.issuer = issuer,
2167 .id = keylet.key,
2168 .holder = owner,
2169 .amount = asset(0)}));
2170 env.close();
2171
2172 env(vault.del({.owner = owner, .id = keylet.key}));
2173 env.close();
2174 });
2175
2176 testCase([&, this](
2177 Env& env,
2178 Account const& owner,
2179 Account const& issuer,
2180 Account const& charlie,
2181 auto,
2182 Vault& vault,
2183 PrettyAsset const& asset,
2184 auto&&...) {
2185 testcase("IOU global freeze");
2186
2187 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2188 env(tx);
2189 env.close();
2190
2191 env(vault.deposit(
2192 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2193 env.close();
2194
2195 env(fset(issuer, asfGlobalFreeze));
2196 env.close();
2197
2198 {
2199 // Cannot withdraw
2200 auto tx = vault.withdraw(
2201 {.depositor = owner,
2202 .id = keylet.key,
2203 .amount = asset(10)});
2204 env(tx, ter{tecFROZEN});
2205
2206 // Cannot withdraw to 3rd party
2207 tx[sfDestination] = charlie.human();
2208 env(tx, ter{tecFROZEN});
2209 env.close();
2210
2211 // Cannot deposit some more
2212 tx = vault.deposit(
2213 {.depositor = owner,
2214 .id = keylet.key,
2215 .amount = asset(10)});
2216
2217 env(tx, ter{tecFROZEN});
2218 }
2219
2220 // Clawback is permitted
2221 env(vault.clawback(
2222 {.issuer = issuer,
2223 .id = keylet.key,
2224 .holder = owner,
2225 .amount = asset(0)}));
2226 env.close();
2227
2228 env(vault.del({.owner = owner, .id = keylet.key}));
2229 env.close();
2230 });
2231 }
2232
2233 void
2235 {
2236 using namespace test::jtx;
2237
2238 testcase("private vault");
2239
2240 Env env{*this, testable_amendments() | featureSingleAssetVault};
2241 Account issuer{"issuer"};
2242 Account owner{"owner"};
2243 Account depositor{"depositor"};
2244 Account charlie{"charlie"};
2245 Account pdOwner{"pdOwner"};
2246 Account credIssuer1{"credIssuer1"};
2247 Account credIssuer2{"credIssuer2"};
2248 std::string const credType = "credential";
2249 Vault vault{env};
2250 env.fund(
2251 XRP(1000),
2252 issuer,
2253 owner,
2254 depositor,
2255 charlie,
2256 pdOwner,
2257 credIssuer1,
2258 credIssuer2);
2259 env.close();
2260 env(fset(issuer, asfAllowTrustLineClawback));
2261 env.close();
2262 env.require(flags(issuer, asfAllowTrustLineClawback));
2263
2264 PrettyAsset asset = issuer["IOU"];
2265 env.trust(asset(1000), owner);
2266 env(pay(issuer, owner, asset(500)));
2267 env.trust(asset(1000), depositor);
2268 env(pay(issuer, depositor, asset(500)));
2269 env.trust(asset(1000), charlie);
2270 env(pay(issuer, charlie, asset(5)));
2271 env.close();
2272
2273 auto [tx, keylet] = vault.create(
2274 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
2275 env(tx);
2276 env.close();
2277 BEAST_EXPECT(env.le(keylet));
2278
2279 {
2280 testcase("private vault owner can deposit");
2281 auto tx = vault.deposit(
2282 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
2283 env(tx);
2284 }
2285
2286 {
2287 testcase("private vault depositor not authorized yet");
2288 auto tx = vault.deposit(
2289 {.depositor = depositor,
2290 .id = keylet.key,
2291 .amount = asset(50)});
2292 env(tx, ter{tecNO_AUTH});
2293 }
2294
2295 {
2296 testcase("private vault cannot set non-existing domain");
2297 auto tx = vault.set({.owner = owner, .id = keylet.key});
2298 tx[sfDomainID] = to_string(base_uint<256>(42ul));
2299 env(tx, ter{tecOBJECT_NOT_FOUND});
2300 }
2301
2302 {
2303 testcase("private vault set domainId");
2304
2305 {
2306 pdomain::Credentials const credentials1{
2307 {.issuer = credIssuer1, .credType = credType}};
2308
2309 env(pdomain::setTx(pdOwner, credentials1));
2310 auto const domainId1 = [&]() {
2311 auto tx = env.tx()->getJson(JsonOptions::none);
2312 return pdomain::getNewDomain(env.meta());
2313 }();
2314
2315 auto tx = vault.set({.owner = owner, .id = keylet.key});
2316 tx[sfDomainID] = to_string(domainId1);
2317 env(tx);
2318 env.close();
2319
2320 // Update domain second time, should be harmless
2321 env(tx);
2322 env.close();
2323 }
2324
2325 {
2326 pdomain::Credentials const credentials{
2327 {.issuer = credIssuer1, .credType = credType},
2328 {.issuer = credIssuer2, .credType = credType}};
2329
2330 env(pdomain::setTx(pdOwner, credentials));
2331 auto const domainId = [&]() {
2332 auto tx = env.tx()->getJson(JsonOptions::none);
2333 return pdomain::getNewDomain(env.meta());
2334 }();
2335
2336 auto tx = vault.set({.owner = owner, .id = keylet.key});
2337 tx[sfDomainID] = to_string(domainId);
2338 env(tx);
2339 env.close();
2340 }
2341 }
2342
2343 {
2344 testcase("private vault depositor still not authorized");
2345 auto tx = vault.deposit(
2346 {.depositor = depositor,
2347 .id = keylet.key,
2348 .amount = asset(50)});
2349 env(tx, ter{tecNO_AUTH});
2350 env.close();
2351 }
2352
2353 auto const credKeylet =
2354 credentials::keylet(depositor, credIssuer1, credType);
2355 {
2356 testcase("private vault depositor now authorized");
2357 env(credentials::create(depositor, credIssuer1, credType));
2358 env(credentials::accept(depositor, credIssuer1, credType));
2359 env(credentials::create(charlie, credIssuer1, credType));
2360 // charlie's credential not accepted
2361 env.close();
2362 auto credSle = env.le(credKeylet);
2363 BEAST_EXPECT(credSle != nullptr);
2364
2365 auto tx = vault.deposit(
2366 {.depositor = depositor,
2367 .id = keylet.key,
2368 .amount = asset(50)});
2369 env(tx);
2370 env.close();
2371
2372 tx = vault.deposit(
2373 {.depositor = charlie, .id = keylet.key, .amount = asset(50)});
2374 env(tx, ter{tecNO_AUTH});
2375 env.close();
2376 }
2377
2378 {
2379 testcase("private vault depositor lost authorization");
2380 env(credentials::deleteCred(
2381 credIssuer1, depositor, credIssuer1, credType));
2382 env(credentials::deleteCred(
2383 credIssuer1, charlie, credIssuer1, credType));
2384 env.close();
2385 auto credSle = env.le(credKeylet);
2386 BEAST_EXPECT(credSle == nullptr);
2387
2388 auto tx = vault.deposit(
2389 {.depositor = depositor,
2390 .id = keylet.key,
2391 .amount = asset(50)});
2392 env(tx, ter{tecNO_AUTH});
2393 env.close();
2394 }
2395
2396 auto const shares = [&env, keylet = keylet, this]() -> Asset {
2397 auto const vault = env.le(keylet);
2398 BEAST_EXPECT(vault != nullptr);
2399 return MPTIssue(vault->at(sfShareMPTID));
2400 }();
2401
2402 {
2403 testcase("private vault expired authorization");
2404 uint32_t const closeTime = env.current()
2405 ->info()
2406 .parentCloseTime.time_since_epoch()
2407 .count();
2408 {
2409 auto tx0 =
2410 credentials::create(depositor, credIssuer2, credType);
2411 tx0[sfExpiration] = closeTime + 20;
2412 env(tx0);
2413 tx0 = credentials::create(charlie, credIssuer2, credType);
2414 tx0[sfExpiration] = closeTime + 20;
2415 env(tx0);
2416 env.close();
2417
2418 env(credentials::accept(depositor, credIssuer2, credType));
2419 env(credentials::accept(charlie, credIssuer2, credType));
2420 env.close();
2421 }
2422
2423 {
2424 auto tx1 = vault.deposit(
2425 {.depositor = depositor,
2426 .id = keylet.key,
2427 .amount = asset(50)});
2428 env(tx1);
2429 env.close();
2430
2431 auto const tokenKeylet = keylet::mptoken(
2432 shares.get<MPTIssue>().getMptID(), depositor.id());
2433 BEAST_EXPECT(env.le(tokenKeylet) != nullptr);
2434 }
2435
2436 {
2437 // time advance
2438 env.close();
2439 env.close();
2440 env.close();
2441
2442 auto const credsKeylet =
2443 credentials::keylet(depositor, credIssuer2, credType);
2444 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
2445
2446 auto tx2 = vault.deposit(
2447 {.depositor = depositor,
2448 .id = keylet.key,
2449 .amount = asset(1)});
2450 env(tx2, ter{tecEXPIRED});
2451 env.close();
2452
2453 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
2454 }
2455
2456 {
2457 auto const credsKeylet =
2458 credentials::keylet(charlie, credIssuer2, credType);
2459 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
2460 auto const tokenKeylet = keylet::mptoken(
2461 shares.get<MPTIssue>().getMptID(), charlie.id());
2462 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
2463
2464 auto tx3 = vault.deposit(
2465 {.depositor = charlie,
2466 .id = keylet.key,
2467 .amount = asset(2)});
2468 env(tx3, ter{tecEXPIRED});
2469
2470 env.close();
2471 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
2472 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
2473 }
2474 }
2475
2476 {
2477 testcase("private vault reset domainId");
2478 auto tx = vault.set({.owner = owner, .id = keylet.key});
2479 tx[sfDomainID] = "0";
2480 env(tx);
2481 env.close();
2482
2483 tx = vault.deposit(
2484 {.depositor = depositor,
2485 .id = keylet.key,
2486 .amount = asset(50)});
2487 env(tx, ter{tecNO_AUTH});
2488 env.close();
2489
2490 tx = vault.withdraw(
2491 {.depositor = depositor,
2492 .id = keylet.key,
2493 .amount = asset(50)});
2494 env(tx);
2495
2496 tx = vault.clawback(
2497 {.issuer = issuer,
2498 .id = keylet.key,
2499 .holder = depositor,
2500 .amount = asset(0)});
2501 env(tx);
2502
2503 tx = vault.clawback(
2504 {.issuer = issuer,
2505 .id = keylet.key,
2506 .holder = owner,
2507 .amount = asset(0)});
2508 env(tx);
2509
2510 tx = vault.del({
2511 .owner = owner,
2512 .id = keylet.key,
2513 });
2514 env(tx);
2515 }
2516 }
2517
2518 void
2520 {
2521 using namespace test::jtx;
2522
2523 testcase("private XRP vault");
2524
2525 Env env{*this, testable_amendments() | featureSingleAssetVault};
2526 Account owner{"owner"};
2527 Account depositor{"depositor"};
2528 Account alice{"charlie"};
2529 std::string const credType = "credential";
2530 Vault vault{env};
2531 env.fund(XRP(100000), owner, depositor, alice);
2532 env.close();
2533
2534 PrettyAsset asset = xrpIssue();
2535 auto [tx, keylet] = vault.create(
2536 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
2537 env(tx);
2538 env.close();
2539
2540 auto const [vaultAccount, issuanceId] =
2541 [&env, keylet = keylet, this]() -> std::tuple<AccountID, uint192> {
2542 auto const vault = env.le(keylet);
2543 BEAST_EXPECT(vault != nullptr);
2544 return {vault->at(sfAccount), vault->at(sfShareMPTID)};
2545 }();
2546 BEAST_EXPECT(env.le(keylet::account(vaultAccount)));
2547 BEAST_EXPECT(env.le(keylet::mptIssuance(issuanceId)));
2548 PrettyAsset shares{issuanceId};
2549
2550 {
2551 testcase("private XRP vault owner can deposit");
2552 auto tx = vault.deposit(
2553 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
2554 env(tx);
2555 }
2556
2557 {
2558 testcase("private XRP vault cannot pay shares to depositor yet");
2559 env(pay(owner, depositor, shares(1)), ter{tecNO_AUTH});
2560 }
2561
2562 {
2563 testcase("private XRP vault depositor not authorized yet");
2564 auto tx = vault.deposit(
2565 {.depositor = depositor,
2566 .id = keylet.key,
2567 .amount = asset(50)});
2568 env(tx, ter{tecNO_AUTH});
2569 }
2570
2571 {
2572 testcase("private XRP vault set DomainID");
2573 pdomain::Credentials const credentials{
2574 {.issuer = owner, .credType = credType}};
2575
2576 env(pdomain::setTx(owner, credentials));
2577 auto const domainId = [&]() {
2578 auto tx = env.tx()->getJson(JsonOptions::none);
2579 return pdomain::getNewDomain(env.meta());
2580 }();
2581
2582 auto tx = vault.set({.owner = owner, .id = keylet.key});
2583 tx[sfDomainID] = to_string(domainId);
2584 env(tx);
2585 env.close();
2586 }
2587
2588 auto const credKeylet = credentials::keylet(depositor, owner, credType);
2589 {
2590 testcase("private XRP vault depositor now authorized");
2591 env(credentials::create(depositor, owner, credType));
2592 env(credentials::accept(depositor, owner, credType));
2593 env.close();
2594
2595 BEAST_EXPECT(env.le(credKeylet));
2596 auto tx = vault.deposit(
2597 {.depositor = depositor,
2598 .id = keylet.key,
2599 .amount = asset(50)});
2600 env(tx);
2601 env.close();
2602 }
2603
2604 {
2605 testcase("private XRP vault can pay shares to depositor");
2606 env(pay(owner, depositor, shares(1)));
2607 }
2608
2609 {
2610 testcase("private XRP vault cannot pay shares to 3rd party");
2611 Json::Value jv;
2612 jv[sfAccount] = alice.human();
2613 jv[sfTransactionType] = jss::MPTokenAuthorize;
2614 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
2615 env(jv);
2616 env.close();
2617
2618 env(pay(owner, alice, shares(1)), ter{tecNO_AUTH});
2619 }
2620 }
2621
2622 void
2624 {
2625 using namespace test::jtx;
2626
2627 testcase("failed pseudo-account allocation");
2628 Env env{*this, testable_amendments() | featureSingleAssetVault};
2629 Account const owner{"owner"};
2630 Vault vault{env};
2631 env.fund(XRP(1000), owner);
2632
2633 auto const keylet = keylet::vault(owner.id(), env.seq(owner));
2634 for (int i = 0; i < 256; ++i)
2635 {
2636 AccountID const accountId =
2637 ripple::pseudoAccountAddress(*env.current(), keylet.key);
2638
2639 env(pay(env.master.id(), accountId, XRP(1000)),
2640 seq(autofill),
2641 fee(autofill),
2642 sig(autofill));
2643 }
2644
2645 auto [tx, keylet1] =
2646 vault.create({.owner = owner, .asset = xrpIssue()});
2647 BEAST_EXPECT(keylet.key == keylet1.key);
2648 env(tx, ter{terADDRESS_COLLISION});
2649 }
2650
2651 void
2653 {
2654 using namespace test::jtx;
2655
2656 testcase("RPC");
2657 Env env{*this, testable_amendments() | featureSingleAssetVault};
2658 Account const owner{"owner"};
2659 Account const issuer{"issuer"};
2660 Vault vault{env};
2661 env.fund(XRP(1000), issuer, owner);
2662 env.close();
2663
2664 PrettyAsset asset = issuer["IOU"];
2665 env.trust(asset(1000), owner);
2666 env(pay(issuer, owner, asset(200)));
2667 env.close();
2668
2669 auto const sequence = env.seq(owner);
2670 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2671 env(tx);
2672 env.close();
2673
2674 // Set some fields
2675 {
2676 auto tx1 = vault.deposit(
2677 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
2678 env(tx1);
2679
2680 auto tx2 = vault.set({.owner = owner, .id = keylet.key});
2681 tx2[sfAssetsMaximum] = asset(1000).number();
2682 env(tx2);
2683 env.close();
2684 }
2685
2686 auto const sleVault = [&env, keylet = keylet, this]() {
2687 auto const vault = env.le(keylet);
2688 BEAST_EXPECT(vault != nullptr);
2689 return vault;
2690 }();
2691
2692 auto const check = [&, keylet = keylet, sle = sleVault, this](
2693 Json::Value const& vault,
2694 Json::Value const& issuance = Json::nullValue) {
2695 BEAST_EXPECT(vault.isObject());
2696
2697 constexpr auto checkString =
2698 [](auto& node, SField const& field, std::string v) -> bool {
2699 return node.isMember(field.fieldName) &&
2700 node[field.fieldName].isString() &&
2701 node[field.fieldName] == v;
2702 };
2703 constexpr auto checkObject =
2704 [](auto& node, SField const& field, Json::Value v) -> bool {
2705 return node.isMember(field.fieldName) &&
2706 node[field.fieldName].isObject() &&
2707 node[field.fieldName] == v;
2708 };
2709 constexpr auto checkInt =
2710 [](auto& node, SField const& field, int v) -> bool {
2711 return node.isMember(field.fieldName) &&
2712 ((node[field.fieldName].isInt() &&
2713 node[field.fieldName] == Json::Int(v)) ||
2714 (node[field.fieldName].isUInt() &&
2715 node[field.fieldName] == Json::UInt(v)));
2716 };
2717
2718 BEAST_EXPECT(vault["LedgerEntryType"].asString() == "Vault");
2719 BEAST_EXPECT(vault[jss::index].asString() == strHex(keylet.key));
2720 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
2721 // Ignore all other standard fields, this test doesn't care
2722
2723 BEAST_EXPECT(
2724 checkString(vault, sfAccount, toBase58(sle->at(sfAccount))));
2725 BEAST_EXPECT(
2726 checkObject(vault, sfAsset, to_json(sle->at(sfAsset))));
2727 BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
2728 BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
2729 BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
2730 BEAST_EXPECT(checkString(vault, sfLossUnrealized, "0"));
2731
2732 auto const strShareID = strHex(sle->at(sfShareMPTID));
2733 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
2734 BEAST_EXPECT(checkString(vault, sfOwner, toBase58(owner.id())));
2735 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
2736 BEAST_EXPECT(checkInt(
2737 vault, sfWithdrawalPolicy, vaultStrategyFirstComeFirstServe));
2738
2739 if (issuance.isObject())
2740 {
2741 BEAST_EXPECT(
2742 issuance["LedgerEntryType"].asString() ==
2743 "MPTokenIssuance");
2744 BEAST_EXPECT(
2745 issuance[jss::mpt_issuance_id].asString() == strShareID);
2746 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
2747 BEAST_EXPECT(checkInt(
2748 issuance,
2749 sfFlags,
2751 BEAST_EXPECT(checkString(issuance, sfOutstandingAmount, "50"));
2752 }
2753 };
2754
2755 {
2756 testcase("RPC ledger_entry selected by key");
2757 Json::Value jvParams;
2758 jvParams[jss::ledger_index] = jss::validated;
2759 jvParams[jss::vault] = strHex(keylet.key);
2760 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2761
2762 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
2763 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
2764 check(jvVault[jss::result][jss::node]);
2765 }
2766
2767 {
2768 testcase("RPC ledger_entry selected by owner and seq");
2769 Json::Value jvParams;
2770 jvParams[jss::ledger_index] = jss::validated;
2771 jvParams[jss::vault][jss::owner] = owner.human();
2772 jvParams[jss::vault][jss::seq] = sequence;
2773 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2774
2775 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
2776 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
2777 check(jvVault[jss::result][jss::node]);
2778 }
2779
2780 {
2781 testcase("RPC ledger_entry cannot find vault by key");
2782 Json::Value jvParams;
2783 jvParams[jss::ledger_index] = jss::validated;
2784 jvParams[jss::vault] = to_string(uint256(42));
2785 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2786 BEAST_EXPECT(
2787 jvVault[jss::result][jss::error].asString() == "entryNotFound");
2788 }
2789
2790 {
2791 testcase("RPC ledger_entry cannot find vault by owner and seq");
2792 Json::Value jvParams;
2793 jvParams[jss::ledger_index] = jss::validated;
2794 jvParams[jss::vault][jss::owner] = issuer.human();
2795 jvParams[jss::vault][jss::seq] = 1'000'000;
2796 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2797 BEAST_EXPECT(
2798 jvVault[jss::result][jss::error].asString() == "entryNotFound");
2799 }
2800
2801 {
2802 testcase("RPC ledger_entry malformed key");
2803 Json::Value jvParams;
2804 jvParams[jss::ledger_index] = jss::validated;
2805 jvParams[jss::vault] = 42;
2806 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2807 BEAST_EXPECT(
2808 jvVault[jss::result][jss::error].asString() ==
2809 "malformedRequest");
2810 }
2811
2812 {
2813 testcase("RPC ledger_entry malformed owner");
2814 Json::Value jvParams;
2815 jvParams[jss::ledger_index] = jss::validated;
2816 jvParams[jss::vault][jss::owner] = 42;
2817 jvParams[jss::vault][jss::seq] = sequence;
2818 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2819 BEAST_EXPECT(
2820 jvVault[jss::result][jss::error].asString() ==
2821 "malformedOwner");
2822 }
2823
2824 {
2825 testcase("RPC ledger_entry malformed seq");
2826 Json::Value jvParams;
2827 jvParams[jss::ledger_index] = jss::validated;
2828 jvParams[jss::vault][jss::owner] = issuer.human();
2829 jvParams[jss::vault][jss::seq] = "foo";
2830 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2831 BEAST_EXPECT(
2832 jvVault[jss::result][jss::error].asString() ==
2833 "malformedRequest");
2834 }
2835
2836 {
2837 testcase("RPC ledger_entry zero seq");
2838 Json::Value jvParams;
2839 jvParams[jss::ledger_index] = jss::validated;
2840 jvParams[jss::vault][jss::owner] = issuer.human();
2841 jvParams[jss::vault][jss::seq] = 0;
2842 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2843 BEAST_EXPECT(
2844 jvVault[jss::result][jss::error].asString() ==
2845 "malformedRequest");
2846 }
2847
2848 {
2849 testcase("RPC ledger_entry negative seq");
2850 Json::Value jvParams;
2851 jvParams[jss::ledger_index] = jss::validated;
2852 jvParams[jss::vault][jss::owner] = issuer.human();
2853 jvParams[jss::vault][jss::seq] = -1;
2854 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2855 BEAST_EXPECT(
2856 jvVault[jss::result][jss::error].asString() ==
2857 "malformedRequest");
2858 }
2859
2860 {
2861 testcase("RPC ledger_entry oversized seq");
2862 Json::Value jvParams;
2863 jvParams[jss::ledger_index] = jss::validated;
2864 jvParams[jss::vault][jss::owner] = issuer.human();
2865 jvParams[jss::vault][jss::seq] = 1e20;
2866 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2867 BEAST_EXPECT(
2868 jvVault[jss::result][jss::error].asString() ==
2869 "malformedRequest");
2870 }
2871
2872 {
2873 testcase("RPC ledger_entry bool seq");
2874 Json::Value jvParams;
2875 jvParams[jss::ledger_index] = jss::validated;
2876 jvParams[jss::vault][jss::owner] = issuer.human();
2877 jvParams[jss::vault][jss::seq] = true;
2878 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2879 BEAST_EXPECT(
2880 jvVault[jss::result][jss::error].asString() ==
2881 "malformedRequest");
2882 }
2883
2884 {
2885 testcase("RPC account_objects");
2886
2887 Json::Value jvParams;
2888 jvParams[jss::account] = owner.human();
2889 jvParams[jss::type] = jss::vault;
2890 auto jv = env.rpc(
2891 "json", "account_objects", to_string(jvParams))[jss::result];
2892
2893 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
2894 check(jv[jss::account_objects][0u]);
2895 }
2896
2897 {
2898 testcase("RPC ledger_data");
2899
2900 Json::Value jvParams;
2901 jvParams[jss::ledger_index] = jss::validated;
2902 jvParams[jss::binary] = false;
2903 jvParams[jss::type] = jss::vault;
2904 Json::Value jv =
2905 env.rpc("json", "ledger_data", to_string(jvParams));
2906 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
2907 check(jv[jss::result][jss::state][0u]);
2908 }
2909
2910 {
2911 testcase("RPC vault_info command line");
2912 Json::Value jv =
2913 env.rpc("vault_info", strHex(keylet.key), "validated");
2914
2915 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
2916 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
2917 check(
2918 jv[jss::result][jss::vault],
2919 jv[jss::result][jss::vault][jss::shares]);
2920 }
2921
2922 {
2923 testcase("RPC vault_info json");
2924 Json::Value jvParams;
2925 jvParams[jss::ledger_index] = jss::validated;
2926 jvParams[jss::vault_id] = strHex(keylet.key);
2927 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
2928
2929 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
2930 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
2931 check(
2932 jv[jss::result][jss::vault],
2933 jv[jss::result][jss::vault][jss::shares]);
2934 }
2935
2936 {
2937 testcase("RPC vault_info invalid vault_id");
2938 Json::Value jvParams;
2939 jvParams[jss::ledger_index] = jss::validated;
2940 jvParams[jss::vault_id] = "foobar";
2941 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
2942 BEAST_EXPECT(
2943 jv[jss::result][jss::error].asString() == "malformedRequest");
2944 }
2945
2946 {
2947 testcase("RPC vault_info json invalid index");
2948 Json::Value jvParams;
2949 jvParams[jss::ledger_index] = jss::validated;
2950 jvParams[jss::vault_id] = 0;
2951 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
2952 BEAST_EXPECT(
2953 jv[jss::result][jss::error].asString() == "malformedRequest");
2954 }
2955
2956 {
2957 testcase("RPC vault_info json by owner and sequence");
2958 Json::Value jvParams;
2959 jvParams[jss::ledger_index] = jss::validated;
2960 jvParams[jss::owner] = owner.human();
2961 jvParams[jss::seq] = sequence;
2962 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
2963
2964 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
2965 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
2966 check(
2967 jv[jss::result][jss::vault],
2968 jv[jss::result][jss::vault][jss::shares]);
2969 }
2970
2971 {
2972 testcase("RPC vault_info json malformed sequence");
2973 Json::Value jvParams;
2974 jvParams[jss::ledger_index] = jss::validated;
2975 jvParams[jss::owner] = owner.human();
2976 jvParams[jss::seq] = "foobar";
2977 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
2978 BEAST_EXPECT(
2979 jv[jss::result][jss::error].asString() == "malformedRequest");
2980 }
2981
2982 {
2983 testcase("RPC vault_info json invalid sequence");
2984 Json::Value jvParams;
2985 jvParams[jss::ledger_index] = jss::validated;
2986 jvParams[jss::owner] = owner.human();
2987 jvParams[jss::seq] = 0;
2988 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
2989 BEAST_EXPECT(
2990 jv[jss::result][jss::error].asString() == "malformedRequest");
2991 }
2992
2993 {
2994 testcase("RPC vault_info json negative sequence");
2995 Json::Value jvParams;
2996 jvParams[jss::ledger_index] = jss::validated;
2997 jvParams[jss::owner] = owner.human();
2998 jvParams[jss::seq] = -1;
2999 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3000 BEAST_EXPECT(
3001 jv[jss::result][jss::error].asString() == "malformedRequest");
3002 }
3003
3004 {
3005 testcase("RPC vault_info json oversized sequence");
3006 Json::Value jvParams;
3007 jvParams[jss::ledger_index] = jss::validated;
3008 jvParams[jss::owner] = owner.human();
3009 jvParams[jss::seq] = 1e20;
3010 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3011 BEAST_EXPECT(
3012 jv[jss::result][jss::error].asString() == "malformedRequest");
3013 }
3014
3015 {
3016 testcase("RPC vault_info json bool sequence");
3017 Json::Value jvParams;
3018 jvParams[jss::ledger_index] = jss::validated;
3019 jvParams[jss::owner] = owner.human();
3020 jvParams[jss::seq] = true;
3021 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3022 BEAST_EXPECT(
3023 jv[jss::result][jss::error].asString() == "malformedRequest");
3024 }
3025
3026 {
3027 testcase("RPC vault_info json malformed owner");
3028 Json::Value jvParams;
3029 jvParams[jss::ledger_index] = jss::validated;
3030 jvParams[jss::owner] = "foobar";
3031 jvParams[jss::seq] = sequence;
3032 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3033 BEAST_EXPECT(
3034 jv[jss::result][jss::error].asString() == "malformedRequest");
3035 }
3036
3037 {
3038 testcase("RPC vault_info json invalid combination only owner");
3039 Json::Value jvParams;
3040 jvParams[jss::ledger_index] = jss::validated;
3041 jvParams[jss::owner] = owner.human();
3042 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3043 BEAST_EXPECT(
3044 jv[jss::result][jss::error].asString() == "malformedRequest");
3045 }
3046
3047 {
3048 testcase("RPC vault_info json invalid combination only seq");
3049 Json::Value jvParams;
3050 jvParams[jss::ledger_index] = jss::validated;
3051 jvParams[jss::seq] = sequence;
3052 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3053 BEAST_EXPECT(
3054 jv[jss::result][jss::error].asString() == "malformedRequest");
3055 }
3056
3057 {
3058 testcase("RPC vault_info json invalid combination seq vault_id");
3059 Json::Value jvParams;
3060 jvParams[jss::ledger_index] = jss::validated;
3061 jvParams[jss::vault_id] = strHex(keylet.key);
3062 jvParams[jss::seq] = sequence;
3063 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3064 BEAST_EXPECT(
3065 jv[jss::result][jss::error].asString() == "malformedRequest");
3066 }
3067
3068 {
3069 testcase("RPC vault_info json invalid combination owner vault_id");
3070 Json::Value jvParams;
3071 jvParams[jss::ledger_index] = jss::validated;
3072 jvParams[jss::vault_id] = strHex(keylet.key);
3073 jvParams[jss::owner] = owner.human();
3074 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3075 BEAST_EXPECT(
3076 jv[jss::result][jss::error].asString() == "malformedRequest");
3077 }
3078
3079 {
3080 testcase(
3081 "RPC vault_info json invalid combination owner seq "
3082 "vault_id");
3083 Json::Value jvParams;
3084 jvParams[jss::ledger_index] = jss::validated;
3085 jvParams[jss::vault_id] = strHex(keylet.key);
3086 jvParams[jss::seq] = sequence;
3087 jvParams[jss::owner] = owner.human();
3088 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3089 BEAST_EXPECT(
3090 jv[jss::result][jss::error].asString() == "malformedRequest");
3091 }
3092
3093 {
3094 testcase("RPC vault_info json no input");
3095 Json::Value jvParams;
3096 jvParams[jss::ledger_index] = jss::validated;
3097 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3098 BEAST_EXPECT(
3099 jv[jss::result][jss::error].asString() == "malformedRequest");
3100 }
3101
3102 {
3103 testcase("RPC vault_info command line invalid index");
3104 Json::Value jv = env.rpc("vault_info", "foobar", "validated");
3105 BEAST_EXPECT(jv[jss::error].asString() == "invalidParams");
3106 }
3107
3108 {
3109 testcase("RPC vault_info command line invalid index");
3110 Json::Value jv = env.rpc("vault_info", "0", "validated");
3111 BEAST_EXPECT(
3112 jv[jss::result][jss::error].asString() == "malformedRequest");
3113 }
3114
3115 {
3116 testcase("RPC vault_info command line invalid index");
3117 Json::Value jv =
3118 env.rpc("vault_info", strHex(uint256(42)), "validated");
3119 BEAST_EXPECT(
3120 jv[jss::result][jss::error].asString() == "entryNotFound");
3121 }
3122
3123 {
3124 testcase("RPC vault_info command line invalid ledger");
3125 Json::Value jv = env.rpc("vault_info", strHex(keylet.key), "0");
3126 BEAST_EXPECT(
3127 jv[jss::result][jss::error].asString() == "lgrNotFound");
3128 }
3129 }
3130
3131public:
3132 void
3133 run() override
3134 {
3135 testSequences();
3136 testPreflight();
3140 testWithMPT();
3141 testWithIOU();
3146 testRPC();
3147 }
3148};
3149
3150BEAST_DEFINE_TESTSUITE_PRIO(Vault, tx, ripple, 1);
3151
3152} // 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 ...