rippled
Loading...
Searching...
No Matches
Delegate_test.cpp
1#include <test/jtx.h>
2#include <test/jtx/CaptureLogs.h>
3#include <test/jtx/delegate.h>
4
5#include <xrpl/protocol/Feature.h>
6#include <xrpl/protocol/Permissions.h>
7
8namespace ripple {
9namespace test {
11{
12 void
14 {
15 testcase("test feature not enabled");
16 using namespace jtx;
17
18 Env env{*this, features};
19 Account gw{"gateway"};
20 Account alice{"alice"};
21 Account bob{"bob"};
22 env.fund(XRP(1000000), gw, alice, bob);
23 env.close();
24
25 auto res = features[featurePermissionDelegationV1_1] ? ter(tesSUCCESS)
27
28 // can not set Delegate when feature disabled
29 env(delegate::set(gw, alice, {"Payment"}), res);
30 env.close();
31
32 // can not send delegating transaction when feature disabled
33 env(pay(gw, bob, XRP(100)), delegate::as(alice), res);
34 }
35
36 void
38 {
39 testcase("test valid request creating, updating, deleting permissions");
40 using namespace jtx;
41
42 Env env(*this);
43 Account gw{"gateway"};
44 Account alice{"alice"};
45 env.fund(XRP(100000), gw, alice);
46 env.close();
47
48 // delegating an empty permission list when the delegate ledger object
49 // does not exist will not create the ledger object
50 env(delegate::set(gw, alice, std::vector<std::string>{}));
51 env.close();
52 auto const entry = delegate::entry(env, gw, alice);
53 BEAST_EXPECT(entry[jss::result][jss::error] == "entryNotFound");
54
55 auto const permissions = std::vector<std::string>{
56 "Payment",
57 "EscrowCreate",
58 "EscrowFinish",
59 "TrustlineAuthorize",
60 "CheckCreate"};
61 env(delegate::set(gw, alice, permissions));
62 env.close();
63
64 // this lambda function is used to compare the json value of ledger
65 // entry response with the given vector of permissions.
66 auto comparePermissions =
67 [&](Json::Value const& jle,
68 std::vector<std::string> const& permissions,
69 Account const& account,
70 Account const& authorize) {
71 BEAST_EXPECT(
72 !jle[jss::result].isMember(jss::error) &&
73 jle[jss::result].isMember(jss::node));
74 BEAST_EXPECT(
75 jle[jss::result][jss::node]["LedgerEntryType"] ==
76 jss::Delegate);
77 BEAST_EXPECT(
78 jle[jss::result][jss::node][jss::Account] ==
79 account.human());
80 BEAST_EXPECT(
81 jle[jss::result][jss::node][sfAuthorize.jsonName] ==
82 authorize.human());
83
84 auto const& jPermissions =
85 jle[jss::result][jss::node][sfPermissions.jsonName];
86 unsigned i = 0;
87 for (auto const& permission : permissions)
88 {
89 BEAST_EXPECT(
90 jPermissions[i][sfPermission.jsonName]
91 [sfPermissionValue.jsonName] == permission);
92 i++;
93 }
94 };
95
96 // get ledger entry with valid parameter
97 comparePermissions(
98 delegate::entry(env, gw, alice), permissions, gw, alice);
99
100 // gw updates permission
101 auto const newPermissions = std::vector<std::string>{
102 "Payment", "AMMCreate", "AMMDeposit", "AMMWithdraw"};
103 env(delegate::set(gw, alice, newPermissions));
104 env.close();
105
106 // get ledger entry again, permissions should be updated to
107 // newPermissions
108 comparePermissions(
109 delegate::entry(env, gw, alice), newPermissions, gw, alice);
110
111 // gw deletes all permissions delegated to alice, this will delete
112 // the
113 // ledger entry
114 env(delegate::set(gw, alice, {}));
115 env.close();
116 auto const jle = delegate::entry(env, gw, alice);
117 BEAST_EXPECT(jle[jss::result][jss::error] == "entryNotFound");
118
119 // alice can delegate permissions to gw as well
120 env(delegate::set(alice, gw, permissions));
121 env.close();
122 comparePermissions(
123 delegate::entry(env, alice, gw), permissions, alice, gw);
124 auto const response = delegate::entry(env, gw, alice);
125 // alice has not been granted any permissions by gw
126 BEAST_EXPECT(response[jss::result][jss::error] == "entryNotFound");
127 }
128
129 void
131 {
132 testcase("test invalid DelegateSet");
133 using namespace jtx;
134
135 Env env(*this, features);
136 Account gw{"gateway"};
137 Account alice{"alice"};
138 Account bob{"bob"};
139 env.fund(XRP(100000), gw, alice, bob);
140 env.close();
141
142 // when permissions size exceeds the limit 10, should return
143 // temARRAY_TOO_LARGE
144 {
145 env(delegate::set(
146 gw,
147 alice,
148 {"Payment",
149 "EscrowCreate",
150 "EscrowFinish",
151 "EscrowCancel",
152 "CheckCreate",
153 "CheckCash",
154 "CheckCancel",
155 "DepositPreauth",
156 "TrustSet",
157 "NFTokenMint",
158 "NFTokenBurn"}),
160 }
161
162 // alice can not authorize herself
163 {
164 env(delegate::set(alice, alice, {"Payment"}), ter(temMALFORMED));
165 }
166
167 // bad fee
168 {
169 Json::Value jv;
170 jv[jss::TransactionType] = jss::DelegateSet;
171 jv[jss::Account] = gw.human();
172 jv[sfAuthorize.jsonName] = alice.human();
173 Json::Value permissionsJson(Json::arrayValue);
174 Json::Value permissionValue;
175 permissionValue[sfPermissionValue.jsonName] = "Payment";
176 Json::Value permissionObj;
177 permissionObj[sfPermission.jsonName] = permissionValue;
178 permissionsJson.append(permissionObj);
179 jv[sfPermissions.jsonName] = permissionsJson;
180 jv[sfFee.jsonName] = -1;
181 env(jv, ter(temBAD_FEE));
182 }
183
184 // when provided permissions contains duplicate values, should return
185 // temMALFORMED
186 {
187 env(delegate::set(
188 gw,
189 alice,
190 {"Payment",
191 "EscrowCreate",
192 "EscrowFinish",
193 "TrustlineAuthorize",
194 "CheckCreate",
195 "TrustlineAuthorize"}),
197 }
198
199 // when authorizing account which does not exist, should return
200 // tecNO_TARGET
201 {
202 env(delegate::set(gw, Account("unknown"), {"Payment"}),
204 }
205
206 // non-delegatable transaction
207 {
208 env(delegate::set(gw, alice, {"SetRegularKey"}), ter(temMALFORMED));
209 env(delegate::set(gw, alice, {"AccountSet"}), ter(temMALFORMED));
210 env(delegate::set(gw, alice, {"SignerListSet"}), ter(temMALFORMED));
211 env(delegate::set(gw, alice, {"DelegateSet"}), ter(temMALFORMED));
212 env(delegate::set(gw, alice, {"EnableAmendment"}),
214 env(delegate::set(gw, alice, {"UNLModify"}), ter(temMALFORMED));
215 env(delegate::set(gw, alice, {"SetFee"}), ter(temMALFORMED));
216 env(delegate::set(gw, alice, {"Batch"}), ter(temMALFORMED));
217 }
218 }
219
220 void
222 {
223 testcase("test reserve");
224 using namespace jtx;
225
226 // test reserve for DelegateSet
227 {
228 Env env(*this);
229 Account alice{"alice"};
230 Account bob{"bob"};
231 Account carol{"carol"};
232
233 env.fund(drops(env.current()->fees().accountReserve(0)), alice);
234 env.fund(
235 drops(env.current()->fees().accountReserve(1)), bob, carol);
236 env.close();
237
238 // alice does not have enough reserve to create Delegate
239 env(delegate::set(alice, bob, {"Payment"}),
241
242 // bob has enough reserve
243 env(delegate::set(bob, alice, {"Payment"}));
244 env.close();
245
246 // now bob create another Delegate, he does not have
247 // enough reserve
248 env(delegate::set(bob, carol, {"Payment"}),
250 }
251
252 // test reserve when sending transaction on behalf of other account
253 {
254 Env env(*this);
255 Account alice{"alice"};
256 Account bob{"bob"};
257
258 env.fund(drops(env.current()->fees().accountReserve(1)), alice);
259 env.fund(drops(env.current()->fees().accountReserve(2)), bob);
260 env.close();
261
262 // alice gives bob permission
263 env(delegate::set(alice, bob, {"DIDSet", "DIDDelete"}));
264 env.close();
265
266 // bob set DID on behalf of alice, but alice does not have enough
267 // reserve
268 env(did::set(alice),
269 did::uri("uri"),
270 delegate::as(bob),
272
273 // bob can set DID for himself because he has enough reserve
274 env(did::set(bob), did::uri("uri"));
275 env.close();
276 }
277 }
278
279 void
281 {
282 testcase("test fee");
283 using namespace jtx;
284
285 Env env(*this);
286 Account alice{"alice"};
287 Account bob{"bob"};
288 Account carol{"carol"};
289 env.fund(XRP(10000), alice, carol);
290 env.fund(XRP(1000), bob);
291 env.close();
292
293 {
294 auto aliceBalance = env.balance(alice);
295 auto bobBalance = env.balance(bob);
296 auto carolBalance = env.balance(carol);
297
298 env(pay(alice, carol, XRP(100)),
299 fee(XRP(2000)),
300 delegate::as(bob),
302 env.close();
303 BEAST_EXPECT(env.balance(alice) == aliceBalance);
304 BEAST_EXPECT(env.balance(bob) == bobBalance);
305 BEAST_EXPECT(env.balance(carol) == carolBalance);
306 }
307
308 env(delegate::set(alice, bob, {"Payment"}));
309 env.close();
310
311 {
312 // Delegate pays the fee
313 auto aliceBalance = env.balance(alice);
314 auto bobBalance = env.balance(bob);
315 auto carolBalance = env.balance(carol);
316
317 auto const sendAmt = XRP(100);
318 auto const feeAmt = XRP(10);
319 env(pay(alice, carol, sendAmt), fee(feeAmt), delegate::as(bob));
320 env.close();
321 BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt);
322 BEAST_EXPECT(env.balance(bob) == bobBalance - feeAmt);
323 BEAST_EXPECT(env.balance(carol) == carolBalance + sendAmt);
324 }
325
326 {
327 // insufficient balance to pay fee
328 auto aliceBalance = env.balance(alice);
329 auto bobBalance = env.balance(bob);
330 auto carolBalance = env.balance(carol);
331
332 env(pay(alice, carol, XRP(100)),
333 fee(XRP(2000)),
334 delegate::as(bob),
336 env.close();
337 BEAST_EXPECT(env.balance(alice) == aliceBalance);
338 BEAST_EXPECT(env.balance(bob) == bobBalance);
339 BEAST_EXPECT(env.balance(carol) == carolBalance);
340 }
341
342 {
343 // fee is paid by Delegate
344 // on context reset (tec error)
345 auto aliceBalance = env.balance(alice);
346 auto bobBalance = env.balance(bob);
347 auto carolBalance = env.balance(carol);
348 auto const feeAmt = XRP(10);
349
350 env(pay(alice, carol, XRP(20000)),
351 fee(feeAmt),
352 delegate::as(bob),
354 env.close();
355 BEAST_EXPECT(env.balance(alice) == aliceBalance);
356 BEAST_EXPECT(env.balance(bob) == bobBalance - feeAmt);
357 BEAST_EXPECT(env.balance(carol) == carolBalance);
358 }
359 }
360
361 void
363 {
364 testcase("test sequence");
365 using namespace jtx;
366
367 Env env(*this);
368 Account alice{"alice"};
369 Account bob{"bob"};
370 Account carol{"carol"};
371 env.fund(XRP(10000), alice, bob, carol);
372 env.close();
373
374 auto aliceSeq = env.seq(alice);
375 auto bobSeq = env.seq(bob);
376 env(delegate::set(alice, bob, {"Payment"}));
377 env(delegate::set(bob, alice, {"Payment"}));
378 env.close();
379 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
380 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
381 aliceSeq = env.seq(alice);
382 bobSeq = env.seq(bob);
383
384 for (auto i = 0; i < 20; ++i)
385 {
386 // bob is the delegated account, his sequence won't increment
387 env(pay(alice, carol, XRP(10)), fee(XRP(10)), delegate::as(bob));
388 env.close();
389 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
390 BEAST_EXPECT(env.seq(bob) == bobSeq);
391 aliceSeq = env.seq(alice);
392
393 // bob sends payment for himself, his sequence will increment
394 env(pay(bob, carol, XRP(10)), fee(XRP(10)));
395 BEAST_EXPECT(env.seq(alice) == aliceSeq);
396 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
397 bobSeq = env.seq(bob);
398
399 // alice is the delegated account, her sequence won't increment
400 env(pay(bob, carol, XRP(10)), fee(XRP(10)), delegate::as(alice));
401 env.close();
402 BEAST_EXPECT(env.seq(alice) == aliceSeq);
403 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
404 bobSeq = env.seq(bob);
405
406 // alice sends payment for herself, her sequence will increment
407 env(pay(alice, carol, XRP(10)), fee(XRP(10)));
408 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
409 BEAST_EXPECT(env.seq(bob) == bobSeq);
410 aliceSeq = env.seq(alice);
411 }
412 }
413
414 void
416 {
417 testcase("test deleting account");
418 using namespace jtx;
419
420 Env env(*this);
421 Account alice{"alice"};
422 Account bob{"bob"};
423 env.fund(XRP(100000), alice, bob);
424 env.close();
425
426 env(delegate::set(alice, bob, {"Payment"}));
427 env.close();
428 BEAST_EXPECT(
429 env.closed()->exists(keylet::delegate(alice.id(), bob.id())));
430
431 for (std::uint32_t i = 0; i < 256; ++i)
432 env.close();
433
434 auto const aliceBalance = env.balance(alice);
435 auto const bobBalance = env.balance(bob);
436
437 // alice deletes account, this will remove the Delegate object
438 auto const deleteFee = drops(env.current()->fees().increment);
439 env(acctdelete(alice, bob), fee(deleteFee));
440 env.close();
441
442 BEAST_EXPECT(!env.closed()->exists(keylet::account(alice.id())));
443 BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(alice.id())));
444 BEAST_EXPECT(env.balance(bob) == bobBalance + aliceBalance - deleteFee);
445
446 BEAST_EXPECT(
447 !env.closed()->exists(keylet::delegate(alice.id(), bob.id())));
448 }
449
450 void
452 {
453 testcase("test delegate transaction");
454 using namespace jtx;
455
456 Env env(*this);
457 Account alice{"alice"};
458 Account bob{"bob"};
459 Account carol{"carol"};
460
461 XRPAmount const baseFee{env.current()->fees().base};
462
463 // use different initial amount to distinguish the source balance
464 env.fund(XRP(10000), alice);
465 env.fund(XRP(20000), bob);
466 env.fund(XRP(30000), carol);
467 env.close();
468
469 auto aliceBalance = env.balance(alice, XRP);
470 auto bobBalance = env.balance(bob, XRP);
471 auto carolBalance = env.balance(carol, XRP);
472
473 // can not send transaction on one's own behalf
474 env(pay(alice, bob, XRP(50)), delegate::as(alice), ter(temBAD_SIGNER));
475 env.require(balance(alice, aliceBalance));
476
477 env(delegate::set(alice, bob, {"Payment"}));
478 env.close();
479 env.require(balance(alice, aliceBalance - drops(baseFee)));
480 aliceBalance = env.balance(alice, XRP);
481
482 // bob pays 50 XRP to carol on behalf of alice
483 env(pay(alice, carol, XRP(50)), delegate::as(bob));
484 env.close();
485 env.require(balance(alice, aliceBalance - XRP(50)));
486 env.require(balance(carol, carolBalance + XRP(50)));
487 // bob pays the fee
488 env.require(balance(bob, bobBalance - drops(baseFee)));
489 aliceBalance = env.balance(alice, XRP);
490 bobBalance = env.balance(bob, XRP);
491 carolBalance = env.balance(carol, XRP);
492
493 // bob pays 50 XRP to bob self on behalf of alice
494 env(pay(alice, bob, XRP(50)), delegate::as(bob));
495 env.close();
496 env.require(balance(alice, aliceBalance - XRP(50)));
497 env.require(balance(bob, bobBalance + XRP(50) - drops(baseFee)));
498 aliceBalance = env.balance(alice, XRP);
499 bobBalance = env.balance(bob, XRP);
500
501 // bob pay 50 XRP to alice herself on behalf of alice
502 env(pay(alice, alice, XRP(50)), delegate::as(bob), ter(temREDUNDANT));
503 env.close();
504
505 // bob does not have permission to create check
506 env(check::create(alice, bob, XRP(10)),
507 delegate::as(bob),
509
510 // carol does not have permission to create check
511 env(check::create(alice, bob, XRP(10)),
512 delegate::as(carol),
514 }
515
516 void
518 {
519 testcase("test payment granular");
520 using namespace jtx;
521
522 // test PaymentMint and PaymentBurn
523 {
524 Env env(*this);
525 Account alice{"alice"};
526 Account bob{"bob"};
527 Account gw{"gateway"};
528 Account gw2{"gateway2"};
529 auto const USD = gw["USD"];
530 auto const EUR = gw2["EUR"];
531
532 env.fund(XRP(10000), alice);
533 env.fund(XRP(20000), bob);
534 env.fund(XRP(40000), gw, gw2);
535 env.trust(USD(200), alice);
536 env.trust(EUR(400), gw);
537 env.close();
538
539 XRPAmount const baseFee{env.current()->fees().base};
540 auto aliceBalance = env.balance(alice, XRP);
541 auto bobBalance = env.balance(bob, XRP);
542 auto gwBalance = env.balance(gw, XRP);
543 auto gw2Balance = env.balance(gw2, XRP);
544
545 // delegate ledger object is not created yet
546 env(pay(gw, alice, USD(50)),
547 delegate::as(bob),
549 env.require(balance(bob, bobBalance));
550
551 // gw gives bob burn permission
552 env(delegate::set(gw, bob, {"PaymentBurn"}));
553 env.close();
554 env.require(balance(gw, gwBalance - drops(baseFee)));
555 gwBalance = env.balance(gw, XRP);
556
557 // bob sends a payment transaction on behalf of gw
558 env(pay(gw, alice, USD(50)),
559 delegate::as(bob),
561 env.close();
562 env.require(balance(bob, bobBalance));
563
564 // gw gives bob mint permission, alice gives bob burn permission
565 env(delegate::set(gw, bob, {"PaymentMint"}));
566 env(delegate::set(alice, bob, {"PaymentBurn"}));
567 env.close();
568 env.require(balance(alice, aliceBalance - drops(baseFee)));
569 env.require(balance(gw, gwBalance - drops(baseFee)));
570 aliceBalance = env.balance(alice, XRP);
571 gwBalance = env.balance(gw, XRP);
572
573 // can not send XRP
574 env(pay(gw, alice, XRP(50)),
575 delegate::as(bob),
577 env.close();
578 env.require(balance(bob, bobBalance));
579
580 // mint 50 USD
581 env(pay(gw, alice, USD(50)), delegate::as(bob));
582 env.close();
583 env.require(balance(bob, bobBalance - drops(baseFee)));
584 env.require(balance(gw, gwBalance));
585 env.require(balance(gw, alice["USD"](-50)));
586 env.require(balance(alice, USD(50)));
587 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
588 bobBalance = env.balance(bob, XRP);
589
590 // burn 30 USD
591 env(pay(alice, gw, USD(30)), delegate::as(bob));
592 env.close();
593 env.require(balance(bob, bobBalance - drops(baseFee)));
594 env.require(balance(gw, gwBalance));
595 env.require(balance(gw, alice["USD"](-20)));
596 env.require(balance(alice, USD(20)));
597 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
598 bobBalance = env.balance(bob, XRP);
599
600 // bob has both mint and burn permissions
601 env(delegate::set(gw, bob, {"PaymentMint", "PaymentBurn"}));
602 env.close();
603 env.require(balance(gw, gwBalance - drops(baseFee)));
604 gwBalance = env.balance(gw, XRP);
605
606 // mint 100 USD for gw
607 env(pay(gw, alice, USD(100)), delegate::as(bob));
608 env.close();
609 env.require(balance(gw, alice["USD"](-120)));
610 env.require(balance(alice, USD(120)));
611 env.require(balance(bob, bobBalance - drops(baseFee)));
612 bobBalance = env.balance(bob, XRP);
613
614 // gw2 pays gw 200 EUR
615 env(pay(gw2, gw, EUR(200)));
616 env.close();
617 env.require(balance(gw2, gw2Balance - drops(baseFee)));
618 gw2Balance = env.balance(gw2, XRP);
619 env.require(balance(gw2, gw["EUR"](-200)));
620 env.require(balance(gw, EUR(200)));
621
622 // burn 100 EUR for gw
623 env(pay(gw, gw2, EUR(100)), delegate::as(bob));
624 env.close();
625 env.require(balance(gw2, gw["EUR"](-100)));
626 env.require(balance(gw, EUR(100)));
627 env.require(balance(bob, bobBalance - drops(baseFee)));
628 env.require(balance(gw, gwBalance));
629 env.require(balance(gw2, gw2Balance));
630 env.require(balance(alice, aliceBalance));
631 }
632
633 // test PaymentMint won't affect Payment transaction level delegation.
634 {
635 Env env(*this);
636 Account alice{"alice"};
637 Account bob{"bob"};
638 Account gw{"gateway"};
639 auto const USD = gw["USD"];
640
641 env.fund(XRP(10000), alice);
642 env.fund(XRP(20000), bob);
643 env.fund(XRP(40000), gw);
644 env.trust(USD(200), alice);
645 env.close();
646
647 XRPAmount const baseFee{env.current()->fees().base};
648
649 auto aliceBalance = env.balance(alice, XRP);
650 auto bobBalance = env.balance(bob, XRP);
651 auto gwBalance = env.balance(gw, XRP);
652
653 // gw gives bob PaymentBurn permission
654 env(delegate::set(gw, bob, {"PaymentBurn"}));
655 env.close();
656 env.require(balance(gw, gwBalance - drops(baseFee)));
657 gwBalance = env.balance(gw, XRP);
658
659 // bob can not mint on behalf of gw because he only has burn
660 // permission
661 env(pay(gw, alice, USD(50)),
662 delegate::as(bob),
664 env.close();
665 env.require(balance(bob, bobBalance));
666
667 // gw gives bob Payment permission as well
668 env(delegate::set(gw, bob, {"PaymentBurn", "Payment"}));
669 env.close();
670 env.require(balance(gw, gwBalance - drops(baseFee)));
671 gwBalance = env.balance(gw, XRP);
672
673 // bob now can mint on behalf of gw
674 env(pay(gw, alice, USD(50)), delegate::as(bob));
675 env.close();
676 env.require(balance(bob, bobBalance - drops(baseFee)));
677 env.require(balance(gw, gwBalance));
678 env.require(balance(alice, aliceBalance));
679 env.require(balance(gw, alice["USD"](-50)));
680 env.require(balance(alice, USD(50)));
681 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
682 }
683
684 // disallow cross currency payment with only PaymentBurn/PaymentMint
685 // permission
686 {
687 Env env(*this, features);
688 Account const alice{"alice"};
689 Account const bob{"bob"};
690 Account const gw{"gateway"};
691 Account const carol{"carol"};
692 auto const USD = gw["USD"];
693
694 env.fund(XRP(10000), alice, bob, carol, gw);
695 env.close();
696 env.trust(USD(50000), alice);
697 env.trust(USD(50000), bob);
698 env.trust(USD(50000), carol);
699 env(pay(gw, alice, USD(10000)));
700 env(pay(gw, bob, USD(10000)));
701 env(pay(gw, carol, USD(10000)));
702 env.close();
703
704 // PaymentMint
705 {
706 env(offer(carol, XRP(100), USD(501)));
707 BEAST_EXPECT(expectOffers(env, carol, 1));
708 env(delegate::set(gw, bob, {"PaymentMint"}));
709 env.close();
710
711 // bob can not send cross currency payment on behalf of the gw,
712 // even with PaymentMint permission and gw being the issuer.
713 env(pay(gw, alice, USD(5000)),
714 sendmax(XRP(1001)),
716 delegate::as(bob),
718 BEAST_EXPECT(expectOffers(env, carol, 1));
719
720 env(pay(gw, alice, USD(5000)),
721 path(~XRP),
723 delegate::as(bob),
725 BEAST_EXPECT(expectOffers(env, carol, 1));
726
727 // succeed with direct payment
728 env(pay(gw, alice, USD(100)), delegate::as(bob));
729 env.close();
730 }
731
732 // PaymentBurn
733 {
734 env(offer(bob, XRP(100), USD(501)));
735 BEAST_EXPECT(expectOffers(env, bob, 1));
736 env(delegate::set(alice, bob, {"PaymentBurn"}));
737 env.close();
738
739 // bob can not send cross currency payment on behalf of alice,
740 // even with PaymentBurn permission and gw being the issuer.
741 env(pay(alice, gw, USD(5000)),
742 sendmax(XRP(1001)),
744 delegate::as(bob),
746 BEAST_EXPECT(expectOffers(env, bob, 1));
747
748 env(pay(alice, gw, USD(5000)),
749 path(~XRP),
751 delegate::as(bob),
753 BEAST_EXPECT(expectOffers(env, bob, 1));
754
755 // succeed with direct payment
756 env(pay(alice, gw, USD(100)), delegate::as(bob));
757 env.close();
758 }
759 }
760
761 // PaymentMint and PaymentBurn for MPT
762 {
763 std::string logs;
764 Env env(*this, features, std::make_unique<CaptureLogs>(&logs));
765 Account const alice{"alice"};
766 Account const bob{"bob"};
767 Account const gw{"gateway"};
768
769 MPTTester mpt(env, gw, {.holders = {alice, bob}});
770 mpt.create(
771 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
772
773 mpt.authorize({.account = alice});
774 mpt.authorize({.account = bob});
775
776 auto const MPT = mpt["MPT"];
777 env(pay(gw, alice, MPT(500)));
778 env(pay(gw, bob, MPT(500)));
779 env.close();
780 auto aliceMPT = env.balance(alice, MPT);
781 auto bobMPT = env.balance(bob, MPT);
782
783 // PaymentMint
784 {
785 env(delegate::set(gw, bob, {"PaymentMint"}));
786 env.close();
787
788 env(pay(gw, alice, MPT(50)), delegate::as(bob));
789 BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT + MPT(50));
790 BEAST_EXPECT(env.balance(bob, MPT) == bobMPT);
791 aliceMPT = env.balance(alice, MPT);
792 }
793
794 // PaymentBurn
795 {
796 env(delegate::set(alice, bob, {"PaymentBurn"}));
797 env.close();
798
799 env(pay(alice, gw, MPT(50)), delegate::as(bob));
800 BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT - MPT(50));
801 BEAST_EXPECT(env.balance(bob, MPT) == bobMPT);
802 aliceMPT = env.balance(alice, MPT);
803 }
804
805 // Grant both granular permissions and tx level permission.
806 {
807 env(delegate::set(
808 alice, bob, {"PaymentBurn", "PaymentMint", "Payment"}));
809 env.close();
810 env(pay(alice, gw, MPT(50)), delegate::as(bob));
811 BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT - MPT(50));
812 BEAST_EXPECT(env.balance(bob, MPT) == bobMPT);
813 aliceMPT = env.balance(alice, MPT);
814 env(pay(alice, bob, MPT(100)), delegate::as(bob));
815 BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT - MPT(100));
816 BEAST_EXPECT(env.balance(bob, MPT) == bobMPT + MPT(100));
817 }
818 }
819 }
820
821 void
823 {
824 testcase("test TrustSet granular permissions");
825 using namespace jtx;
826
827 // test TrustlineUnfreeze, TrustlineFreeze and TrustlineAuthorize
828 {
829 Env env(*this);
830 Account gw{"gw"};
831 Account alice{"alice"};
832 Account bob{"bob"};
833 env.fund(XRP(10000), gw, alice, bob);
834 env(fset(gw, asfRequireAuth));
835 env.close();
836
837 env(delegate::set(alice, bob, {"TrustlineUnfreeze"}));
838 env.close();
839 // bob can not create trustline on behalf of alice because he only
840 // has unfreeze permission
841 env(trust(alice, gw["USD"](50)),
842 delegate::as(bob),
844 env.close();
845
846 // alice creates trustline by herself
847 env(trust(alice, gw["USD"](50)));
848 env.close();
849
850 // gw gives bob unfreeze permission
851 env(delegate::set(gw, bob, {"TrustlineUnfreeze"}));
852 env.close();
853
854 // unsupported flags
855 env(trust(alice, gw["USD"](50), tfSetNoRipple),
856 delegate::as(bob),
858 env(trust(alice, gw["USD"](50), tfClearNoRipple),
859 delegate::as(bob),
861 env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze),
862 delegate::as(bob),
864 env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze),
865 delegate::as(bob),
867 env.close();
868
869 // supported flags with wrong permission
870 env(trust(gw, gw["USD"](0), alice, tfSetfAuth),
871 delegate::as(bob),
873 env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
874 delegate::as(bob),
876 env.close();
877
878 env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
879 env.close();
880 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
881 delegate::as(bob),
883 env.close();
884 // although trustline authorize is granted, bob can not change the
885 // limit number
886 env(trust(gw, gw["USD"](50), alice, tfSetfAuth),
887 delegate::as(bob),
889 env.close();
890
891 // supported flags with correct permission
892 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::as(bob));
893 env.close();
894 env(delegate::set(
895 gw, bob, {"TrustlineAuthorize", "TrustlineFreeze"}));
896 env.close();
897 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
898 env.close();
899 env(delegate::set(
900 gw, bob, {"TrustlineAuthorize", "TrustlineUnfreeze"}));
901 env.close();
902 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
903 delegate::as(bob));
904 env.close();
905 // but bob can not freeze trustline because he no longer has freeze
906 // permission
907 env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
908 delegate::as(bob),
910
911 // cannot update LimitAmount with granular permission, both high and
912 // low account
913 env(trust(alice, gw["USD"](100)),
914 delegate::as(bob),
916 env(trust(gw, alice["USD"](100)),
917 delegate::as(bob),
919
920 // can not set QualityIn or QualityOut
921 auto tx = trust(alice, gw["USD"](50));
922 tx["QualityIn"] = "1000";
924 auto tx2 = trust(alice, gw["USD"](50));
925 tx2["QualityOut"] = "1000";
927 auto tx3 = trust(gw, alice["USD"](50));
928 tx3["QualityIn"] = "1000";
930 auto tx4 = trust(gw, alice["USD"](50));
931 tx4["QualityOut"] = "1000";
933
934 // granting TrustSet can make it work
935 env(delegate::set(gw, bob, {"TrustSet"}));
936 env.close();
937 auto tx5 = trust(gw, alice["USD"](50));
938 tx5["QualityOut"] = "1000";
939 env(tx5, delegate::as(bob));
940 auto tx6 = trust(alice, gw["USD"](50));
941 tx6["QualityOut"] = "1000";
943 env(delegate::set(alice, bob, {"TrustSet"}));
944 env.close();
945 env(tx6, delegate::as(bob));
946 }
947
948 // test mix of transaction level delegation and granular delegation
949 {
950 Env env(*this);
951 Account gw{"gw"};
952 Account alice{"alice"};
953 Account bob{"bob"};
954 env.fund(XRP(10000), gw, alice, bob);
955 env(fset(gw, asfRequireAuth));
956 env.close();
957
958 // bob does not have permission
959 env(trust(alice, gw["USD"](50)),
960 delegate::as(bob),
962 env(delegate::set(
963 alice, bob, {"TrustlineUnfreeze", "NFTokenCreateOffer"}));
964 env.close();
965 // bob still does not have permission
966 env(trust(alice, gw["USD"](50)),
967 delegate::as(bob),
969
970 // add TrustSet permission and some unrelated permission
971 env(delegate::set(
972 alice,
973 bob,
974 {"TrustlineUnfreeze",
975 "NFTokenCreateOffer",
976 "TrustSet",
977 "AccountTransferRateSet"}));
978 env.close();
979 env(trust(alice, gw["USD"](50)), delegate::as(bob));
980 env.close();
981
982 env(delegate::set(
983 gw,
984 bob,
985 {"TrustlineUnfreeze",
986 "NFTokenCreateOffer",
987 "TrustSet",
988 "AccountTransferRateSet"}));
989 env.close();
990
991 // since bob has TrustSet permission, he does not need
992 // TrustlineFreeze granular permission to freeze the trustline
993 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
994 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
995 delegate::as(bob));
996 // bob can perform all the operations regarding TrustSet
997 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
998 env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze),
999 delegate::as(bob));
1000 env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze),
1001 delegate::as(bob));
1002 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::as(bob));
1003 env(trust(alice, gw["USD"](50), tfSetNoRipple), delegate::as(bob));
1004 env(trust(alice, gw["USD"](50), tfClearNoRipple),
1005 delegate::as(bob));
1006 }
1007
1008 // tfFullyCanonicalSig won't block delegated transaction
1009 {
1010 Env env(*this);
1011 Account gw{"gw"};
1012 Account alice{"alice"};
1013 Account bob{"bob"};
1014 env.fund(XRP(10000), gw, alice, bob);
1015 env(fset(gw, asfRequireAuth));
1016 env.close();
1017 env(trust(alice, gw["USD"](50)));
1018 env.close();
1019
1020 env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
1021 env.close();
1022 env(trust(
1023 gw, gw["USD"](0), alice, tfSetfAuth | tfFullyCanonicalSig),
1024 delegate::as(bob));
1025 }
1026 }
1027
1028 void
1030 {
1031 testcase("test AccountSet granular permissions");
1032 using namespace jtx;
1033
1034 // test AccountDomainSet, AccountEmailHashSet,
1035 // AccountMessageKeySet,AccountTransferRateSet, and AccountTickSizeSet
1036 // granular permissions
1037 {
1038 Env env(*this);
1039 auto const alice = Account{"alice"};
1040 auto const bob = Account{"bob"};
1041 env.fund(XRP(10000), alice, bob);
1042 env.close();
1043
1044 // alice gives bob some random permission, which is not related to
1045 // the AccountSet transaction
1046 env(delegate::set(alice, bob, {"TrustlineUnfreeze"}));
1047 env.close();
1048
1049 // bob does not have permission to set domain
1050 // on behalf of alice
1051 std::string const domain = "example.com";
1052 auto jt = noop(alice);
1053 jt[sfDomain] = strHex(domain);
1054 jt[sfDelegate] = bob.human();
1055
1056 // add granular permission related to AccountSet but is not the
1057 // correct permission for domain set
1058 env(delegate::set(
1059 alice, bob, {"TrustlineUnfreeze", "AccountEmailHashSet"}));
1060 env.close();
1062
1063 // alice give granular permission of AccountDomainSet to bob
1064 env(delegate::set(alice, bob, {"AccountDomainSet"}));
1065 env.close();
1066
1067 // bob set account domain on behalf of alice
1068 env(jt);
1069 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
1070
1071 // bob can reset domain
1072 jt[sfDomain] = "";
1073 env(jt);
1074 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfDomain));
1075
1076 // bob tries to set unauthorized flag, it will fail
1077 std::string const failDomain = "fail_domain_update";
1078 jt[sfFlags] = tfRequireAuth;
1079 jt[sfDomain] = strHex(failDomain);
1081 // reset flag number
1082 jt[sfFlags] = 0;
1083
1084 // bob tries to update domain and set email hash,
1085 // but he does not have permission to set email hash
1086 jt[sfDomain] = strHex(domain);
1087 std::string const mh("5F31A79367DC3137FADA860C05742EE6");
1088 jt[sfEmailHash] = mh;
1090
1091 // alice give granular permission of AccountEmailHashSet to bob
1092 env(delegate::set(
1093 alice, bob, {"AccountDomainSet", "AccountEmailHashSet"}));
1094 env.close();
1095 env(jt);
1096 BEAST_EXPECT(to_string((*env.le(alice))[sfEmailHash]) == mh);
1097 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
1098
1099 // bob does not have permission to set message key for alice
1100 auto const rkp = randomKeyPair(KeyType::ed25519);
1101 jt[sfMessageKey] = strHex(rkp.first.slice());
1103
1104 // alice give granular permission of AccountMessageKeySet to bob
1105 env(delegate::set(
1106 alice,
1107 bob,
1108 {"AccountDomainSet",
1109 "AccountEmailHashSet",
1110 "AccountMessageKeySet"}));
1111 env.close();
1112
1113 // bob can set message key for alice
1114 env(jt);
1115 BEAST_EXPECT(
1116 strHex((*env.le(alice))[sfMessageKey]) ==
1117 strHex(rkp.first.slice()));
1118 jt[sfMessageKey] = "";
1119 env(jt);
1120 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfMessageKey));
1121
1122 // bob does not have permission to set transfer rate for alice
1123 env(rate(alice, 2.0),
1124 delegate::as(bob),
1126
1127 // alice give granular permission of AccountTransferRateSet to bob
1128 env(delegate::set(
1129 alice,
1130 bob,
1131 {"AccountDomainSet",
1132 "AccountEmailHashSet",
1133 "AccountMessageKeySet",
1134 "AccountTransferRateSet"}));
1135 env.close();
1136 auto jtRate = rate(alice, 2.0);
1137 jtRate[sfDelegate] = bob.human();
1138 env(jtRate, delegate::as(bob));
1139 BEAST_EXPECT((*env.le(alice))[sfTransferRate] == 2000000000);
1140
1141 // bob does not have permission to set ticksize for alice
1142 jt[sfTickSize] = 8;
1144
1145 // alice give granular permission of AccountTickSizeSet to bob
1146 env(delegate::set(
1147 alice,
1148 bob,
1149 {"AccountDomainSet",
1150 "AccountEmailHashSet",
1151 "AccountMessageKeySet",
1152 "AccountTransferRateSet",
1153 "AccountTickSizeSet"}));
1154 env.close();
1155 env(jt);
1156 BEAST_EXPECT((*env.le(alice))[sfTickSize] == 8);
1157
1158 // can not set asfRequireAuth flag for alice
1159 env(fset(alice, asfRequireAuth),
1160 delegate::as(bob),
1162
1163 // reset Delegate will delete the Delegate
1164 // object
1165 env(delegate::set(alice, bob, {}));
1166 // bib still does not have permission to set asfRequireAuth for
1167 // alice
1168 env(fset(alice, asfRequireAuth),
1169 delegate::as(bob),
1171 // alice can set for herself
1172 env(fset(alice, asfRequireAuth));
1173 env.require(flags(alice, asfRequireAuth));
1174 env.close();
1175
1176 // can not update tick size because bob no longer has permission
1177 jt[sfTickSize] = 7;
1179
1180 env(delegate::set(
1181 alice,
1182 bob,
1183 {"AccountDomainSet",
1184 "AccountEmailHashSet",
1185 "AccountMessageKeySet"}));
1186 env.close();
1187
1188 // bob does not have permission to set wallet locater for alice
1189 std::string const locator =
1190 "9633EC8AF54F16B5286DB1D7B519EF49EEFC050C0C8AC4384F1D88ACD1BFDF"
1191 "05";
1192 auto jv2 = noop(alice);
1193 jv2[sfDomain] = strHex(domain);
1194 jv2[sfDelegate] = bob.human();
1195 jv2[sfWalletLocator] = locator;
1196 env(jv2, ter(terNO_DELEGATE_PERMISSION));
1197 }
1198
1199 // can not set AccountSet flags on behalf of other account
1200 {
1201 Env env(*this);
1202 auto const alice = Account{"alice"};
1203 auto const bob = Account{"bob"};
1204 env.fund(XRP(10000), alice, bob);
1205 env.close();
1206
1207 auto testSetClearFlag = [&](std::uint32_t flag) {
1208 // bob can not set flag on behalf of alice
1209 env(fset(alice, flag),
1210 delegate::as(bob),
1212 // alice set by herself
1213 env(fset(alice, flag));
1214 env.close();
1215 env.require(flags(alice, flag));
1216 // bob can not clear on behalf of alice
1217 env(fclear(alice, flag),
1218 delegate::as(bob),
1220 };
1221
1222 // testSetClearFlag(asfNoFreeze);
1223 testSetClearFlag(asfRequireAuth);
1224 testSetClearFlag(asfAllowTrustLineClawback);
1225
1226 // alice gives some granular permissions to bob
1227 env(delegate::set(
1228 alice,
1229 bob,
1230 {"AccountDomainSet",
1231 "AccountEmailHashSet",
1232 "AccountMessageKeySet"}));
1233 env.close();
1234
1235 testSetClearFlag(asfDefaultRipple);
1236 testSetClearFlag(asfDepositAuth);
1237 testSetClearFlag(asfDisallowIncomingCheck);
1238 testSetClearFlag(asfDisallowIncomingNFTokenOffer);
1239 testSetClearFlag(asfDisallowIncomingPayChan);
1240 testSetClearFlag(asfDisallowIncomingTrustline);
1241 testSetClearFlag(asfDisallowXRP);
1242 testSetClearFlag(asfRequireDest);
1243 testSetClearFlag(asfGlobalFreeze);
1244
1245 // bob can not set asfAccountTxnID on behalf of alice
1246 env(fset(alice, asfAccountTxnID),
1247 delegate::as(bob),
1249 env(fset(alice, asfAccountTxnID));
1250 env.close();
1251 BEAST_EXPECT(env.le(alice)->isFieldPresent(sfAccountTxnID));
1252 env(fclear(alice, asfAccountTxnID),
1253 delegate::as(bob),
1255
1256 // bob can not set asfAuthorizedNFTokenMinter on behalf of alice
1258 jt[sfDelegate] = bob.human();
1259 jt[sfNFTokenMinter] = bob.human();
1261
1262 // bob gives alice some permissions
1263 env(delegate::set(
1264 bob,
1265 alice,
1266 {"AccountDomainSet",
1267 "AccountEmailHashSet",
1268 "AccountMessageKeySet"}));
1269 env.close();
1270
1271 // since we can not set asfNoFreeze if asfAllowTrustLineClawback is
1272 // set, which can not be clear either. Test alice set asfNoFreeze on
1273 // behalf of bob.
1274 env(fset(alice, asfNoFreeze),
1275 delegate::as(bob),
1277 env(fset(bob, asfNoFreeze));
1278 env.close();
1279 env.require(flags(bob, asfNoFreeze));
1280 // alice can not clear on behalf of bob
1281 env(fclear(alice, asfNoFreeze),
1282 delegate::as(bob),
1284
1285 // bob can not set asfDisableMaster on behalf of alice
1286 Account const bobKey{"bobKey", KeyType::secp256k1};
1287 env(regkey(bob, bobKey));
1288 env.close();
1289 env(fset(alice, asfDisableMaster),
1290 delegate::as(bob),
1291 sig(bob),
1293 }
1294
1295 // tfFullyCanonicalSig won't block delegated transaction
1296 {
1297 Env env(*this);
1298 Account alice{"alice"};
1299 Account bob{"bob"};
1300 env.fund(XRP(10000), alice, bob);
1301 env.close();
1302
1303 env(delegate::set(
1304 alice, bob, {"AccountDomainSet", "AccountEmailHashSet"}));
1305 env.close();
1306
1307 std::string const domain = "example.com";
1308 auto jt = noop(alice);
1309 jt[sfDomain] = strHex(domain);
1310 jt[sfDelegate] = bob.human();
1311 jt[sfFlags] = tfFullyCanonicalSig;
1312
1313 env(jt);
1314 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
1315 }
1316 }
1317
1318 void
1320 {
1321 testcase("test MPTokenIssuanceSet granular");
1322 using namespace jtx;
1323
1324 // test MPTokenIssuanceUnlock and MPTokenIssuanceLock permissions
1325 {
1326 Env env(*this);
1327 Account alice{"alice"};
1328 Account bob{"bob"};
1329 env.fund(XRP(100000), alice, bob);
1330 env.close();
1331
1332 MPTTester mpt(env, alice, {.fund = false});
1333 env.close();
1334 mpt.create({.flags = tfMPTCanLock});
1335 env.close();
1336
1337 // delegate ledger object is not created yet
1338 mpt.set(
1339 {.account = alice,
1340 .flags = tfMPTLock,
1341 .delegate = bob,
1343
1344 // alice gives granular permission to bob of MPTokenIssuanceUnlock
1345 env(delegate::set(alice, bob, {"MPTokenIssuanceUnlock"}));
1346 env.close();
1347 // bob does not have lock permission
1348 mpt.set(
1349 {.account = alice,
1350 .flags = tfMPTLock,
1351 .delegate = bob,
1353 // bob now has lock permission, but does not have unlock permission
1354 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
1355 env.close();
1356 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1357 mpt.set(
1358 {.account = alice,
1359 .flags = tfMPTUnlock,
1360 .delegate = bob,
1362
1363 // now bob can lock and unlock
1364 env(delegate::set(
1365 alice, bob, {"MPTokenIssuanceLock", "MPTokenIssuanceUnlock"}));
1366 env.close();
1367 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1368 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1369 env.close();
1370 }
1371
1372 // test mix of granular and transaction level permission
1373 {
1374 Env env(*this);
1375 Account alice{"alice"};
1376 Account bob{"bob"};
1377 env.fund(XRP(100000), alice, bob);
1378 env.close();
1379
1380 MPTTester mpt(env, alice, {.fund = false});
1381 env.close();
1382 mpt.create({.flags = tfMPTCanLock});
1383 env.close();
1384
1385 // alice gives granular permission to bob of MPTokenIssuanceLock
1386 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
1387 env.close();
1388 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1389 // bob does not have unlock permission
1390 mpt.set(
1391 {.account = alice,
1392 .flags = tfMPTUnlock,
1393 .delegate = bob,
1395
1396 // alice gives bob some unrelated permission with
1397 // MPTokenIssuanceLock
1398 env(delegate::set(
1399 alice,
1400 bob,
1401 {"NFTokenMint", "MPTokenIssuanceLock", "NFTokenBurn"}));
1402 env.close();
1403 // bob can not unlock
1404 mpt.set(
1405 {.account = alice,
1406 .flags = tfMPTUnlock,
1407 .delegate = bob,
1409
1410 // alice add MPTokenIssuanceSet to permissions
1411 env(delegate::set(
1412 alice,
1413 bob,
1414 {"NFTokenMint",
1415 "MPTokenIssuanceLock",
1416 "NFTokenBurn",
1417 "MPTokenIssuanceSet"}));
1418 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1419 // alice can lock by herself
1420 mpt.set({.account = alice, .flags = tfMPTLock});
1421 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1422 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1423 }
1424
1425 // tfFullyCanonicalSig won't block delegated transaction
1426 {
1427 Env env(*this);
1428 Account alice{"alice"};
1429 Account bob{"bob"};
1430 env.fund(XRP(100000), alice, bob);
1431 env.close();
1432
1433 MPTTester mpt(env, alice, {.fund = false});
1434 env.close();
1435 mpt.create({.flags = tfMPTCanLock});
1436 env.close();
1437
1438 // alice gives granular permission to bob of MPTokenIssuanceLock
1439 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
1440 env.close();
1441 mpt.set(
1442 {.account = alice,
1443 .flags = tfMPTLock | tfFullyCanonicalSig,
1444 .delegate = bob});
1445 }
1446 }
1447
1448 void
1450 {
1451 testcase("test single sign");
1452 using namespace jtx;
1453
1454 Env env(*this);
1455 Account alice{"alice"};
1456 Account bob{"bob"};
1457 Account carol{"carol"};
1458 env.fund(XRP(100000), alice, bob, carol);
1459 env.close();
1460
1461 env(delegate::set(alice, bob, {"Payment"}));
1462 env.close();
1463
1464 auto aliceBalance = env.balance(alice);
1465 auto bobBalance = env.balance(bob);
1466 auto carolBalance = env.balance(carol);
1467
1468 env(pay(alice, carol, XRP(100)),
1469 fee(XRP(10)),
1470 delegate::as(bob),
1471 sig(bob));
1472 env.close();
1473 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100));
1474 BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10));
1475 BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(100));
1476 }
1477
1478 void
1480 {
1481 testcase("test single sign with bad secret");
1482 using namespace jtx;
1483
1484 {
1485 Env env(*this);
1486 Account alice{"alice"};
1487 Account bob{"bob"};
1488 Account carol{"carol"};
1489 env.fund(XRP(100000), alice, bob, carol);
1490 env.close();
1491
1492 env(delegate::set(alice, bob, {"Payment"}));
1493 env.close();
1494
1495 auto aliceBalance = env.balance(alice);
1496 auto bobBalance = env.balance(bob);
1497 auto carolBalance = env.balance(carol);
1498
1499 env(pay(alice, carol, XRP(100)),
1500 fee(XRP(10)),
1501 delegate::as(bob),
1502 sig(alice),
1503 ter(tefBAD_AUTH));
1504 env.close();
1505 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1506 BEAST_EXPECT(env.balance(bob) == bobBalance);
1507 BEAST_EXPECT(env.balance(carol) == carolBalance);
1508 }
1509
1510 {
1511 Env env(*this);
1512 Account alice{"alice"}, bob{"bob"}, carol{"carol"};
1513 env.fund(XRP(100000), alice, bob, carol);
1514 env.close();
1515
1516 env(delegate::set(alice, bob, {"TrustSet"}));
1517 env.close();
1518
1519 auto aliceBalance = env.balance(alice);
1520 auto bobBalance = env.balance(bob);
1521 auto carolBalance = env.balance(carol);
1522
1523 env(pay(alice, carol, XRP(100)),
1524 fee(XRP(10)),
1525 delegate::as(bob),
1526 sig(carol),
1528 env.close();
1529 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1530 BEAST_EXPECT(env.balance(bob) == bobBalance);
1531 BEAST_EXPECT(env.balance(carol) == carolBalance);
1532
1533 env(pay(alice, carol, XRP(100)),
1534 fee(XRP(10)),
1535 delegate::as(bob),
1536 sig(alice),
1538 env.close();
1539 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1540 BEAST_EXPECT(env.balance(bob) == bobBalance);
1541 BEAST_EXPECT(env.balance(carol) == carolBalance);
1542 }
1543
1544 {
1545 Env env(*this);
1546 Account alice{"alice"}, bob{"bob"}, carol{"carol"};
1547 env.fund(XRP(100000), alice, bob, carol);
1548 env.close();
1549
1550 auto aliceBalance = env.balance(alice);
1551 auto bobBalance = env.balance(bob);
1552 auto carolBalance = env.balance(carol);
1553
1554 env(pay(alice, carol, XRP(100)),
1555 fee(XRP(10)),
1556 delegate::as(bob),
1557 sig(alice),
1559 env.close();
1560 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1561 BEAST_EXPECT(env.balance(bob) == bobBalance);
1562 BEAST_EXPECT(env.balance(carol) == carolBalance);
1563
1564 env(pay(alice, carol, XRP(100)),
1565 fee(XRP(10)),
1566 delegate::as(bob),
1567 sig(carol),
1569 env.close();
1570 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1571 BEAST_EXPECT(env.balance(bob) == bobBalance);
1572 BEAST_EXPECT(env.balance(carol) == carolBalance);
1573 }
1574 }
1575
1576 void
1578 {
1579 testcase("test multi sign");
1580 using namespace jtx;
1581
1582 Env env(*this);
1583 Account alice{"alice"};
1584 Account bob{"bob"};
1585 Account carol{"carol"};
1586 Account daria{"daria"};
1587 Account edward{"edward"};
1588 env.fund(XRP(100000), alice, bob, carol, daria, edward);
1589 env.close();
1590
1591 env(signers(bob, 2, {{daria, 1}, {edward, 1}}));
1592 env.close();
1593
1594 env(delegate::set(alice, bob, {"Payment"}));
1595 env.close();
1596
1597 auto aliceBalance = env.balance(alice);
1598 auto bobBalance = env.balance(bob);
1599 auto carolBalance = env.balance(carol);
1600 auto dariaBalance = env.balance(daria);
1601 auto edwardBalance = env.balance(edward);
1602
1603 env(pay(alice, carol, XRP(100)),
1604 fee(XRP(10)),
1605 delegate::as(bob),
1606 msig(daria, edward));
1607 env.close();
1608 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100));
1609 BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10));
1610 BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(100));
1611 BEAST_EXPECT(env.balance(daria) == dariaBalance);
1612 BEAST_EXPECT(env.balance(edward) == edwardBalance);
1613 }
1614
1615 void
1617 {
1618 testcase("test multi sign which does not meet quorum");
1619 using namespace jtx;
1620
1621 Env env(*this);
1622 Account alice{"alice"};
1623 Account bob{"bob"};
1624 Account carol{"carol"};
1625 Account daria = Account{"daria"};
1626 Account edward = Account{"edward"};
1627 Account fred = Account{"fred"};
1628 env.fund(XRP(100000), alice, bob, carol, daria, edward, fred);
1629 env.close();
1630
1631 env(signers(bob, 3, {{daria, 1}, {edward, 1}, {fred, 1}}));
1632 env.close();
1633
1634 env(delegate::set(alice, bob, {"Payment"}));
1635 env.close();
1636
1637 auto aliceBalance = env.balance(alice);
1638 auto bobBalance = env.balance(bob);
1639 auto carolBalance = env.balance(carol);
1640 auto dariaBalance = env.balance(daria);
1641 auto edwardBalance = env.balance(edward);
1642
1643 env(pay(alice, carol, XRP(100)),
1644 fee(XRP(10)),
1645 delegate::as(bob),
1646 msig(daria, edward),
1648 env.close();
1649 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1650 BEAST_EXPECT(env.balance(bob) == bobBalance);
1651 BEAST_EXPECT(env.balance(carol) == carolBalance);
1652 BEAST_EXPECT(env.balance(daria) == dariaBalance);
1653 BEAST_EXPECT(env.balance(edward) == edwardBalance);
1654 }
1655
1656 void
1658 {
1659 testcase("test permission value");
1660 using namespace jtx;
1661
1662 Env env(*this, features);
1663
1664 Account alice{"alice"};
1665 Account bob{"bob"};
1666 env.fund(XRP(100000), alice, bob);
1667 env.close();
1668
1669 auto buildRequest = [&](auto value) -> Json::Value {
1670 Json::Value jv;
1671 jv[jss::TransactionType] = jss::DelegateSet;
1672 jv[jss::Account] = alice.human();
1673 jv[sfAuthorize.jsonName] = bob.human();
1674
1675 Json::Value permissionsJson(Json::arrayValue);
1676 Json::Value permissionValue;
1677 permissionValue[sfPermissionValue.jsonName] = value;
1678 Json::Value permissionObj;
1679 permissionObj[sfPermission.jsonName] = permissionValue;
1680 permissionsJson.append(permissionObj);
1681 jv[sfPermissions.jsonName] = permissionsJson;
1682
1683 return jv;
1684 };
1685
1686 // invalid permission value.
1687 // neither granular permission nor transaction level permission
1688 for (auto value : {0, 100000, 54321})
1689 {
1690 auto jv = buildRequest(value);
1691 env(jv, ter(temMALFORMED));
1692 }
1693 }
1694
1695 void
1697 {
1698 testcase("test delegate disabled tx");
1699 using namespace jtx;
1700
1701 // map of tx and required feature.
1702 // non-delegatable tx are not included.
1703 // NFTokenMint, NFTokenBurn, NFTokenCreateOffer, NFTokenCancelOffer,
1704 // NFTokenAcceptOffer are not included, they are tested separately.
1706 {"TicketCreate", featureTicketBatch},
1707 {"CheckCreate", featureChecks},
1708 {"CheckCash", featureChecks},
1709 {"CheckCancel", featureChecks},
1710 {"DepositPreauth", featureDepositPreauth},
1711 {"Clawback", featureClawback},
1712 {"AMMClawback", featureAMMClawback},
1713 {"AMMCreate", featureAMM},
1714 {"AMMDeposit", featureAMM},
1715 {"AMMWithdraw", featureAMM},
1716 {"AMMVote", featureAMM},
1717 {"AMMBid", featureAMM},
1718 {"AMMDelete", featureAMM},
1719 {"XChainCreateClaimID", featureXChainBridge},
1720 {"XChainCommit", featureXChainBridge},
1721 {"XChainClaim", featureXChainBridge},
1722 {"XChainAccountCreateCommit", featureXChainBridge},
1723 {"XChainAddClaimAttestation", featureXChainBridge},
1724 {"XChainAddAccountCreateAttestation", featureXChainBridge},
1725 {"XChainModifyBridge", featureXChainBridge},
1726 {"XChainCreateBridge", featureXChainBridge},
1727 {"DIDSet", featureDID},
1728 {"DIDDelete", featureDID},
1729 {"OracleSet", featurePriceOracle},
1730 {"OracleDelete", featurePriceOracle},
1731 {"LedgerStateFix", fixNFTokenPageLinks},
1732 {"MPTokenIssuanceCreate", featureMPTokensV1},
1733 {"MPTokenIssuanceDestroy", featureMPTokensV1},
1734 {"MPTokenIssuanceSet", featureMPTokensV1},
1735 {"MPTokenAuthorize", featureMPTokensV1},
1736 {"CredentialCreate", featureCredentials},
1737 {"CredentialAccept", featureCredentials},
1738 {"CredentialDelete", featureCredentials},
1739 {"NFTokenModify", featureDynamicNFT},
1740 {"PermissionedDomainSet", featurePermissionedDomains},
1741 {"PermissionedDomainDelete", featurePermissionedDomains},
1742 {"VaultCreate", featureSingleAssetVault},
1743 {"VaultSet", featureSingleAssetVault},
1744 {"VaultDelete", featureSingleAssetVault},
1745 {"VaultDeposit", featureSingleAssetVault},
1746 {"VaultWithdraw", featureSingleAssetVault},
1747 {"VaultClawback", featureSingleAssetVault}};
1748
1749 // Can not delegate tx if any required feature disabled.
1750 {
1751 auto txAmendmentDisabled = [&](FeatureBitset features,
1752 std::string const& tx) {
1753 BEAST_EXPECT(txRequiredFeatures.contains(tx));
1754
1755 Env env(*this, features - txRequiredFeatures[tx]);
1756
1757 Account const alice{"alice"};
1758 Account const bob{"bob"};
1759 env.fund(XRP(100000), alice, bob);
1760 env.close();
1761
1762 env(delegate::set(alice, bob, {tx}), ter(temMALFORMED));
1763 };
1764
1765 for (auto const& tx : txRequiredFeatures)
1766 txAmendmentDisabled(features, tx.first);
1767 }
1768
1769 // if all the required features in txRequiredFeatures are enabled, will
1770 // succeed
1771 {
1772 auto txAmendmentEnabled = [&](std::string const& tx) {
1773 Env env(*this, features);
1774
1775 Account const alice{"alice"};
1776 Account const bob{"bob"};
1777 env.fund(XRP(100000), alice, bob);
1778 env.close();
1779
1780 env(delegate::set(alice, bob, {tx}));
1781 };
1782
1783 for (auto const& tx : txRequiredFeatures)
1784 txAmendmentEnabled(tx.first);
1785 }
1786 }
1787
1788 void
1813};
1814BEAST_DEFINE_TESTSUITE(Delegate, app, ripple);
1815} // namespace test
1816} // namespace ripple
Represents a JSON value.
Definition json_value.h:130
Value & append(Value const &value)
Append value to array at the end.
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
void run() override
Runs the suite.
void testPermissionValue(FeatureBitset features)
void testFeatureDisabled(FeatureBitset features)
void testTxReqireFeatures(FeatureBitset features)
void testInvalidRequest(FeatureBitset features)
void testPaymentGranular(FeatureBitset features)
Immutable cryptographic account descriptor.
Definition Account.h:20
A transaction testing environment.
Definition Env.h:102
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition Env.cpp:97
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:250
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:528
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:312
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:103
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:302
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:271
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:165
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:259
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:68
Converts to MPT Issue or STAmount.
A balance matches.
Definition balance.h:20
Sets the optional URI on a DIDSet.
Definition did.h:41
Set the domain on a JTx.
Definition domain.h:11
Set the fee on a JTx.
Definition fee.h:18
Match set account flags.
Definition flags.h:109
Set a multisignature on a JTx.
Definition multisign.h:48
Add a path.
Definition paths.h:39
Sets the SendMax on a JTx.
Definition sendmax.h:14
Set the regular signature on a JTx.
Definition sig.h:16
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:16
Set the flags on a JTx.
Definition txflags.h:12
T is_same_v
@ arrayValue
array value (ordered list)
Definition json_value.h:25
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Definition Indexes.cpp:446
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:355
Json::Value create(A const &account, A const &dest, STAmount const &sendMax)
Create a check.
Json::Value entry(jtx::Env &env, jtx::Account const &account, jtx::Account const &authorize)
Definition delegate.cpp:36
Json::Value set(jtx::Account const &account, jtx::Account const &authorize, std::vector< std::string > const &permissions)
Definition delegate.cpp:12
Json::Value set(jtx::Account const &account)
Definition dids.cpp:14
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:102
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition regkey.cpp:10
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
bool expectOffers(Env &env, AccountID const &account, std::uint16_t size, std::vector< Amounts > const &toMatch)
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:55
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
constexpr std::uint32_t asfGlobalFreeze
Definition TxFlags.h:64
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:66
constexpr std::uint32_t asfDisallowIncomingNFTokenOffer
Definition TxFlags.h:71
constexpr std::uint32_t tfSetDeepFreeze
Definition TxFlags.h:101
constexpr std::uint32_t const tfMPTCanTransfer
Definition TxFlags.h:133
constexpr std::uint32_t asfRequireDest
Definition TxFlags.h:58
constexpr std::uint32_t const tfMPTUnlock
Definition TxFlags.h:158
constexpr std::uint32_t asfAuthorizedNFTokenMinter
Definition TxFlags.h:67
constexpr std::uint32_t asfNoFreeze
Definition TxFlags.h:63
constexpr std::uint32_t asfDisableMaster
Definition TxFlags.h:61
constexpr std::uint32_t asfDisallowIncomingTrustline
Definition TxFlags.h:74
@ tefBAD_AUTH
Definition TER.h:150
@ tefBAD_QUORUM
Definition TER.h:161
constexpr std::uint32_t tfPartialPayment
Definition TxFlags.h:89
constexpr std::uint32_t tfClearNoRipple
Definition TxFlags.h:98
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
constexpr std::uint32_t tfSetfAuth
Definition TxFlags.h:96
constexpr std::uint32_t asfAccountTxnID
Definition TxFlags.h:62
constexpr std::uint32_t asfDefaultRipple
Definition TxFlags.h:65
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:225
constexpr std::uint32_t asfDisallowIncomingCheck
Definition TxFlags.h:72
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:100
constexpr std::uint32_t tfRequireAuth
Definition TxFlags.h:49
@ tecNO_TARGET
Definition TER.h:286
@ tecUNFUNDED_PAYMENT
Definition TER.h:267
@ tecINSUFFICIENT_RESERVE
Definition TER.h:289
constexpr std::uint32_t const tfMPTLock
Definition TxFlags.h:157
@ tesSUCCESS
Definition TER.h:226
constexpr std::uint32_t tfClearDeepFreeze
Definition TxFlags.h:102
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
constexpr std::uint32_t asfDisallowIncomingPayChan
Definition TxFlags.h:73
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
constexpr std::uint32_t tfFullyCanonicalSig
Transaction flags.
Definition TxFlags.h:41
constexpr std::uint32_t asfAllowTrustLineClawback
Definition TxFlags.h:75
constexpr std::uint32_t asfRequireAuth
Definition TxFlags.h:59
@ terINSUF_FEE_B
Definition TER.h:197
@ terNO_DELEGATE_PERMISSION
Definition TER.h:211
constexpr std::uint32_t tfSetFreeze
Definition TxFlags.h:99
constexpr std::uint32_t tfSetNoRipple
Definition TxFlags.h:97
constexpr std::uint32_t const tfMPTCanLock
Definition TxFlags.h:129
constexpr std::uint32_t asfDisallowXRP
Definition TxFlags.h:60
@ temREDUNDANT
Definition TER.h:93
@ temBAD_FEE
Definition TER.h:73
@ temBAD_SIGNER
Definition TER.h:96
@ temMALFORMED
Definition TER.h:68
@ temARRAY_TOO_LARGE
Definition TER.h:122
@ temDISABLED
Definition TER.h:95