rippled
Loading...
Searching...
No Matches
Delegate_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2025 Ripple Labs Inc.
5 Permission to use, copy, modify, and/or distribute this software for any
6 purpose with or without fee is hereby granted, provided that the above
7 copyright notice and this permission notice appear in all copies.
8 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*/
16//==============================================================================
17
18#include <test/jtx.h>
19#include <test/jtx/delegate.h>
20
21#include <xrpl/protocol/Feature.h>
22#include <xrpl/protocol/Permissions.h>
23
24namespace ripple {
25namespace test {
27{
28 void
30 {
31 testcase("test featurePermissionDelegation not enabled");
32 using namespace jtx;
33
34 Env env{*this, supported_amendments() - featurePermissionDelegation};
35 Account gw{"gateway"};
36 Account alice{"alice"};
37 Account bob{"bob"};
38 env.fund(XRP(1000000), gw, alice, bob);
39 env.close();
40
41 // can not set Delegate when feature disabled
42 env(delegate::set(gw, alice, {"Payment"}), ter(temDISABLED));
43
44 // can not send delegating transaction when feature disabled
45 env(pay(alice, bob, XRP(100)), delegate::as(bob), ter(temDISABLED));
46 }
47
48 void
50 {
51 testcase("test valid request creating, updating, deleting permissions");
52 using namespace jtx;
53
54 Env env(*this);
55 Account gw{"gateway"};
56 Account alice{"alice"};
57 env.fund(XRP(100000), gw, alice);
58 env.close();
59
60 // delegating an empty permission list when the delegate ledger object
61 // does not exist will not create the ledger object
62 env(delegate::set(gw, alice, std::vector<std::string>{}));
63 env.close();
64 auto const entry = delegate::entry(env, gw, alice);
65 BEAST_EXPECT(entry[jss::result][jss::error] == "entryNotFound");
66
67 auto const permissions = std::vector<std::string>{
68 "Payment",
69 "EscrowCreate",
70 "EscrowFinish",
71 "TrustlineAuthorize",
72 "CheckCreate"};
73 env(delegate::set(gw, alice, permissions));
74 env.close();
75
76 // this lambda function is used to compare the json value of ledger
77 // entry response with the given vector of permissions.
78 auto comparePermissions =
79 [&](Json::Value const& jle,
80 std::vector<std::string> const& permissions,
81 Account const& account,
82 Account const& authorize) {
83 BEAST_EXPECT(
84 !jle[jss::result].isMember(jss::error) &&
85 jle[jss::result].isMember(jss::node));
86 BEAST_EXPECT(
87 jle[jss::result][jss::node]["LedgerEntryType"] ==
88 jss::Delegate);
89 BEAST_EXPECT(
90 jle[jss::result][jss::node][jss::Account] ==
91 account.human());
92 BEAST_EXPECT(
93 jle[jss::result][jss::node][sfAuthorize.jsonName] ==
94 authorize.human());
95
96 auto const& jPermissions =
97 jle[jss::result][jss::node][sfPermissions.jsonName];
98 unsigned i = 0;
99 for (auto const& permission : permissions)
100 {
101 BEAST_EXPECT(
102 jPermissions[i][sfPermission.jsonName]
103 [sfPermissionValue.jsonName] == permission);
104 i++;
105 }
106 };
107
108 // get ledger entry with valid parameter
109 comparePermissions(
110 delegate::entry(env, gw, alice), permissions, gw, alice);
111
112 // gw updates permission
113 auto const newPermissions = std::vector<std::string>{
114 "Payment", "AMMCreate", "AMMDeposit", "AMMWithdraw"};
115 env(delegate::set(gw, alice, newPermissions));
116 env.close();
117
118 // get ledger entry again, permissions should be updated to
119 // newPermissions
120 comparePermissions(
121 delegate::entry(env, gw, alice), newPermissions, gw, alice);
122
123 // gw deletes all permissions delegated to alice, this will delete
124 // the
125 // ledger entry
126 env(delegate::set(gw, alice, {}));
127 env.close();
128 auto const jle = delegate::entry(env, gw, alice);
129 BEAST_EXPECT(jle[jss::result][jss::error] == "entryNotFound");
130
131 // alice can delegate permissions to gw as well
132 env(delegate::set(alice, gw, permissions));
133 env.close();
134 comparePermissions(
135 delegate::entry(env, alice, gw), permissions, alice, gw);
136 auto const response = delegate::entry(env, gw, alice);
137 // alice has not been granted any permissions by gw
138 BEAST_EXPECT(response[jss::result][jss::error] == "entryNotFound");
139 }
140
141 void
143 {
144 testcase("test invalid DelegateSet");
145 using namespace jtx;
146
147 Env env(*this);
148 Account gw{"gateway"};
149 Account alice{"alice"};
150 Account bob{"bob"};
151 env.fund(XRP(100000), gw, alice, bob);
152 env.close();
153
154 // when permissions size exceeds the limit 10, should return
155 // temARRAY_TOO_LARGE
156 {
157 env(delegate::set(
158 gw,
159 alice,
160 {"Payment",
161 "EscrowCreate",
162 "EscrowFinish",
163 "EscrowCancel",
164 "CheckCreate",
165 "CheckCash",
166 "CheckCancel",
167 "DepositPreauth",
168 "TrustSet",
169 "NFTokenMint",
170 "NFTokenBurn"}),
172 }
173
174 // alice can not authorize herself
175 {
176 env(delegate::set(alice, alice, {"Payment"}), ter(temMALFORMED));
177 }
178
179 // bad fee
180 {
181 Json::Value jv;
182 jv[jss::TransactionType] = jss::DelegateSet;
183 jv[jss::Account] = gw.human();
184 jv[sfAuthorize.jsonName] = alice.human();
185 Json::Value permissionsJson(Json::arrayValue);
186 Json::Value permissionValue;
187 permissionValue[sfPermissionValue.jsonName] = "Payment";
188 Json::Value permissionObj;
189 permissionObj[sfPermission.jsonName] = permissionValue;
190 permissionsJson.append(permissionObj);
191 jv[sfPermissions.jsonName] = permissionsJson;
192 jv[sfFee.jsonName] = -1;
193 env(jv, ter(temBAD_FEE));
194 }
195
196 // when provided permissions contains duplicate values, should return
197 // temMALFORMED
198 {
199 env(delegate::set(
200 gw,
201 alice,
202 {"Payment",
203 "EscrowCreate",
204 "EscrowFinish",
205 "TrustlineAuthorize",
206 "CheckCreate",
207 "TrustlineAuthorize"}),
209 }
210
211 // when authorizing account which does not exist, should return
212 // terNO_ACCOUNT
213 {
214 env(delegate::set(gw, Account("unknown"), {"Payment"}),
216 }
217
218 // non-delegatable transaction
219 {
220 env(delegate::set(gw, alice, {"SetRegularKey"}),
222 env(delegate::set(gw, alice, {"AccountSet"}),
224 env(delegate::set(gw, alice, {"SignerListSet"}),
226 env(delegate::set(gw, alice, {"DelegateSet"}),
228 env(delegate::set(gw, alice, {"SetRegularKey"}),
230 env(delegate::set(gw, alice, {"EnableAmendment"}),
232 env(delegate::set(gw, alice, {"UNLModify"}), ter(tecNO_PERMISSION));
233 env(delegate::set(gw, alice, {"SetFee"}), ter(tecNO_PERMISSION));
234 env(delegate::set(gw, alice, {"Batch"}), ter(tecNO_PERMISSION));
235 }
236 }
237
238 void
240 {
241 testcase("test reserve");
242 using namespace jtx;
243
244 // test reserve for DelegateSet
245 {
246 Env env(*this);
247 Account alice{"alice"};
248 Account bob{"bob"};
249 Account carol{"carol"};
250
251 env.fund(drops(env.current()->fees().accountReserve(0)), alice);
252 env.fund(
253 drops(env.current()->fees().accountReserve(1)), bob, carol);
254 env.close();
255
256 // alice does not have enough reserve to create Delegate
257 env(delegate::set(alice, bob, {"Payment"}),
259
260 // bob has enough reserve
261 env(delegate::set(bob, alice, {"Payment"}));
262 env.close();
263
264 // now bob create another Delegate, he does not have
265 // enough reserve
266 env(delegate::set(bob, carol, {"Payment"}),
268 }
269
270 // test reserve when sending transaction on behalf of other account
271 {
272 Env env(*this);
273 Account alice{"alice"};
274 Account bob{"bob"};
275
276 env.fund(drops(env.current()->fees().accountReserve(1)), alice);
277 env.fund(drops(env.current()->fees().accountReserve(2)), bob);
278 env.close();
279
280 // alice gives bob permission
281 env(delegate::set(alice, bob, {"DIDSet", "DIDDelete"}));
282 env.close();
283
284 // bob set DID on behalf of alice, but alice does not have enough
285 // reserve
286 env(did::set(alice),
287 did::uri("uri"),
288 delegate::as(bob),
290
291 // bob can set DID for himself because he has enough reserve
292 env(did::set(bob), did::uri("uri"));
293 env.close();
294 }
295 }
296
297 void
299 {
300 testcase("test fee");
301 using namespace jtx;
302
303 Env env(*this);
304 Account alice{"alice"};
305 Account bob{"bob"};
306 Account carol{"carol"};
307 env.fund(XRP(10000), alice, carol);
308 env.fund(XRP(1000), bob);
309 env.close();
310
311 {
312 // Fee should be checked before permission check,
313 // otherwise tecNO_PERMISSION returned when permission check fails
314 // could cause context reset to pay fee because it is tec error
315 auto aliceBalance = env.balance(alice);
316 auto bobBalance = env.balance(bob);
317 auto carolBalance = env.balance(carol);
318
319 env(pay(alice, carol, XRP(100)),
320 fee(XRP(2000)),
321 delegate::as(bob),
323 env.close();
324 BEAST_EXPECT(env.balance(alice) == aliceBalance);
325 BEAST_EXPECT(env.balance(bob) == bobBalance);
326 BEAST_EXPECT(env.balance(carol) == carolBalance);
327 }
328
329 env(delegate::set(alice, bob, {"Payment"}));
330 env.close();
331
332 {
333 // Delegate pays the fee
334 auto aliceBalance = env.balance(alice);
335 auto bobBalance = env.balance(bob);
336 auto carolBalance = env.balance(carol);
337
338 auto const sendAmt = XRP(100);
339 auto const feeAmt = XRP(10);
340 env(pay(alice, carol, sendAmt), fee(feeAmt), delegate::as(bob));
341 env.close();
342 BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt);
343 BEAST_EXPECT(env.balance(bob) == bobBalance - feeAmt);
344 BEAST_EXPECT(env.balance(carol) == carolBalance + sendAmt);
345 }
346
347 {
348 // insufficient balance to pay fee
349 auto aliceBalance = env.balance(alice);
350 auto bobBalance = env.balance(bob);
351 auto carolBalance = env.balance(carol);
352
353 env(pay(alice, carol, XRP(100)),
354 fee(XRP(2000)),
355 delegate::as(bob),
357 env.close();
358 BEAST_EXPECT(env.balance(alice) == aliceBalance);
359 BEAST_EXPECT(env.balance(bob) == bobBalance);
360 BEAST_EXPECT(env.balance(carol) == carolBalance);
361 }
362
363 {
364 // fee is paid by Delegate
365 // on context reset (tec error)
366 auto aliceBalance = env.balance(alice);
367 auto bobBalance = env.balance(bob);
368 auto carolBalance = env.balance(carol);
369 auto const feeAmt = XRP(10);
370
371 env(pay(alice, carol, XRP(20000)),
372 fee(feeAmt),
373 delegate::as(bob),
375 env.close();
376 BEAST_EXPECT(env.balance(alice) == aliceBalance);
377 BEAST_EXPECT(env.balance(bob) == bobBalance - feeAmt);
378 BEAST_EXPECT(env.balance(carol) == carolBalance);
379 }
380 }
381
382 void
384 {
385 testcase("test sequence");
386 using namespace jtx;
387
388 Env env(*this);
389 Account alice{"alice"};
390 Account bob{"bob"};
391 Account carol{"carol"};
392 env.fund(XRP(10000), alice, bob, carol);
393 env.close();
394
395 auto aliceSeq = env.seq(alice);
396 auto bobSeq = env.seq(bob);
397 env(delegate::set(alice, bob, {"Payment"}));
398 env(delegate::set(bob, alice, {"Payment"}));
399 env.close();
400 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
401 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
402 aliceSeq = env.seq(alice);
403 bobSeq = env.seq(bob);
404
405 for (auto i = 0; i < 20; ++i)
406 {
407 // bob is the delegated account, his sequence won't increment
408 env(pay(alice, carol, XRP(10)), fee(XRP(10)), delegate::as(bob));
409 env.close();
410 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
411 BEAST_EXPECT(env.seq(bob) == bobSeq);
412 aliceSeq = env.seq(alice);
413
414 // bob sends payment for himself, his sequence will increment
415 env(pay(bob, carol, XRP(10)), fee(XRP(10)));
416 BEAST_EXPECT(env.seq(alice) == aliceSeq);
417 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
418 bobSeq = env.seq(bob);
419
420 // alice is the delegated account, her sequence won't increment
421 env(pay(bob, carol, XRP(10)), fee(XRP(10)), delegate::as(alice));
422 env.close();
423 BEAST_EXPECT(env.seq(alice) == aliceSeq);
424 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
425 bobSeq = env.seq(bob);
426
427 // alice sends payment for herself, her sequence will increment
428 env(pay(alice, carol, XRP(10)), fee(XRP(10)));
429 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
430 BEAST_EXPECT(env.seq(bob) == bobSeq);
431 aliceSeq = env.seq(alice);
432 }
433 }
434
435 void
437 {
438 testcase("test deleting account");
439 using namespace jtx;
440
441 Env env(*this);
442 Account alice{"alice"};
443 Account bob{"bob"};
444 env.fund(XRP(100000), alice, bob);
445 env.close();
446
447 env(delegate::set(alice, bob, {"Payment"}));
448 env.close();
449 BEAST_EXPECT(
450 env.closed()->exists(keylet::delegate(alice.id(), bob.id())));
451
452 for (std::uint32_t i = 0; i < 256; ++i)
453 env.close();
454
455 auto const aliceBalance = env.balance(alice);
456 auto const bobBalance = env.balance(bob);
457
458 // alice deletes account, this will remove the Delegate object
459 auto const deleteFee = drops(env.current()->fees().increment);
460 env(acctdelete(alice, bob), fee(deleteFee));
461 env.close();
462
463 BEAST_EXPECT(!env.closed()->exists(keylet::account(alice.id())));
464 BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(alice.id())));
465 BEAST_EXPECT(env.balance(bob) == bobBalance + aliceBalance - deleteFee);
466
467 BEAST_EXPECT(
468 !env.closed()->exists(keylet::delegate(alice.id(), bob.id())));
469 }
470
471 void
473 {
474 testcase("test delegate transaction");
475 using namespace jtx;
476
477 Env env(*this);
478 Account alice{"alice"};
479 Account bob{"bob"};
480 Account carol{"carol"};
481
482 XRPAmount const baseFee{env.current()->fees().base};
483
484 // use different initial amount to distinguish the source balance
485 env.fund(XRP(10000), alice);
486 env.fund(XRP(20000), bob);
487 env.fund(XRP(30000), carol);
488 env.close();
489
490 auto aliceBalance = env.balance(alice, XRP);
491 auto bobBalance = env.balance(bob, XRP);
492 auto carolBalance = env.balance(carol, XRP);
493
494 // can not send transaction on one's own behalf
495 env(pay(alice, bob, XRP(50)), delegate::as(alice), ter(temBAD_SIGNER));
496 env.require(balance(alice, aliceBalance));
497
498 env(delegate::set(alice, bob, {"Payment"}));
499 env.close();
500 env.require(balance(alice, aliceBalance - drops(baseFee)));
501 aliceBalance = env.balance(alice, XRP);
502
503 // bob pays 50 XRP to carol on behalf of alice
504 env(pay(alice, carol, XRP(50)), delegate::as(bob));
505 env.close();
506 env.require(balance(alice, aliceBalance - XRP(50)));
507 env.require(balance(carol, carolBalance + XRP(50)));
508 // bob pays the fee
509 env.require(balance(bob, bobBalance - drops(baseFee)));
510 aliceBalance = env.balance(alice, XRP);
511 bobBalance = env.balance(bob, XRP);
512 carolBalance = env.balance(carol, XRP);
513
514 // bob pays 50 XRP to bob self on behalf of alice
515 env(pay(alice, bob, XRP(50)), delegate::as(bob));
516 env.close();
517 env.require(balance(alice, aliceBalance - XRP(50)));
518 env.require(balance(bob, bobBalance + XRP(50) - drops(baseFee)));
519 aliceBalance = env.balance(alice, XRP);
520 bobBalance = env.balance(bob, XRP);
521
522 // bob pay 50 XRP to alice herself on behalf of alice
523 env(pay(alice, alice, XRP(50)), delegate::as(bob), ter(temREDUNDANT));
524 env.close();
525
526 // bob does not have permission to create check
527 env(check::create(alice, bob, XRP(10)),
528 delegate::as(bob),
530
531 // carol does not have permission to create check
532 env(check::create(alice, bob, XRP(10)),
533 delegate::as(carol),
535 }
536
537 void
539 {
540 testcase("test payment granular");
541 using namespace jtx;
542
543 // test PaymentMint and PaymentBurn
544 {
545 Env env(*this);
546 Account alice{"alice"};
547 Account bob{"bob"};
548 Account gw{"gateway"};
549 Account gw2{"gateway2"};
550 auto const USD = gw["USD"];
551 auto const EUR = gw2["EUR"];
552
553 env.fund(XRP(10000), alice);
554 env.fund(XRP(20000), bob);
555 env.fund(XRP(40000), gw, gw2);
556 env.trust(USD(200), alice);
557 env.trust(EUR(400), gw);
558 env.close();
559
560 XRPAmount const baseFee{env.current()->fees().base};
561 auto aliceBalance = env.balance(alice, XRP);
562 auto bobBalance = env.balance(bob, XRP);
563 auto gwBalance = env.balance(gw, XRP);
564 auto gw2Balance = env.balance(gw2, XRP);
565
566 // delegate ledger object is not created yet
567 env(pay(gw, alice, USD(50)),
568 delegate::as(bob),
570 env.require(balance(bob, bobBalance - drops(baseFee)));
571 bobBalance = env.balance(bob, XRP);
572
573 // gw gives bob burn permission
574 env(delegate::set(gw, bob, {"PaymentBurn"}));
575 env.close();
576 env.require(balance(gw, gwBalance - drops(baseFee)));
577 gwBalance = env.balance(gw, XRP);
578
579 // bob sends a payment transaction on behalf of gw
580 env(pay(gw, alice, USD(50)),
581 delegate::as(bob),
583 env.close();
584 env.require(balance(bob, bobBalance - drops(baseFee)));
585 bobBalance = env.balance(bob, XRP);
586
587 // gw gives bob mint permission, alice gives bob burn permission
588 env(delegate::set(gw, bob, {"PaymentMint"}));
589 env(delegate::set(alice, bob, {"PaymentBurn"}));
590 env.close();
591 env.require(balance(alice, aliceBalance - drops(baseFee)));
592 env.require(balance(gw, gwBalance - drops(baseFee)));
593 aliceBalance = env.balance(alice, XRP);
594 gwBalance = env.balance(gw, XRP);
595
596 // can not send XRP
597 env(pay(gw, alice, XRP(50)),
598 delegate::as(bob),
600 env.close();
601 env.require(balance(bob, bobBalance - drops(baseFee)));
602 bobBalance = env.balance(bob, XRP);
603
604 // mint 50 USD
605 env(pay(gw, alice, USD(50)), delegate::as(bob));
606 env.close();
607 env.require(balance(bob, bobBalance - drops(baseFee)));
608 env.require(balance(gw, gwBalance));
609 env.require(balance(gw, alice["USD"](-50)));
610 env.require(balance(alice, USD(50)));
611 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
612 bobBalance = env.balance(bob, XRP);
613
614 // burn 30 USD
615 env(pay(alice, gw, USD(30)), delegate::as(bob));
616 env.close();
617 env.require(balance(bob, bobBalance - drops(baseFee)));
618 env.require(balance(gw, gwBalance));
619 env.require(balance(gw, alice["USD"](-20)));
620 env.require(balance(alice, USD(20)));
621 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
622 bobBalance = env.balance(bob, XRP);
623
624 // bob has both mint and burn permissions
625 env(delegate::set(gw, bob, {"PaymentMint", "PaymentBurn"}));
626 env.close();
627 env.require(balance(gw, gwBalance - drops(baseFee)));
628 gwBalance = env.balance(gw, XRP);
629
630 // mint 100 USD for gw
631 env(pay(gw, alice, USD(100)), delegate::as(bob));
632 env.close();
633 env.require(balance(gw, alice["USD"](-120)));
634 env.require(balance(alice, USD(120)));
635 env.require(balance(bob, bobBalance - drops(baseFee)));
636 bobBalance = env.balance(bob, XRP);
637
638 // gw2 pays gw 200 EUR
639 env(pay(gw2, gw, EUR(200)));
640 env.close();
641 env.require(balance(gw2, gw2Balance - drops(baseFee)));
642 gw2Balance = env.balance(gw2, XRP);
643 env.require(balance(gw2, gw["EUR"](-200)));
644 env.require(balance(gw, EUR(200)));
645
646 // burn 100 EUR for gw
647 env(pay(gw, gw2, EUR(100)), delegate::as(bob));
648 env.close();
649 env.require(balance(gw2, gw["EUR"](-100)));
650 env.require(balance(gw, EUR(100)));
651 env.require(balance(bob, bobBalance - drops(baseFee)));
652 env.require(balance(gw, gwBalance));
653 env.require(balance(gw2, gw2Balance));
654 env.require(balance(alice, aliceBalance));
655 }
656
657 // test PaymentMint won't affect Payment transaction level delegation.
658 {
659 Env env(*this);
660 Account alice{"alice"};
661 Account bob{"bob"};
662 Account gw{"gateway"};
663 auto const USD = gw["USD"];
664
665 env.fund(XRP(10000), alice);
666 env.fund(XRP(20000), bob);
667 env.fund(XRP(40000), gw);
668 env.trust(USD(200), alice);
669 env.close();
670
671 XRPAmount const baseFee{env.current()->fees().base};
672
673 auto aliceBalance = env.balance(alice, XRP);
674 auto bobBalance = env.balance(bob, XRP);
675 auto gwBalance = env.balance(gw, XRP);
676
677 // gw gives bob PaymentBurn permission
678 env(delegate::set(gw, bob, {"PaymentBurn"}));
679 env.close();
680 env.require(balance(gw, gwBalance - drops(baseFee)));
681 gwBalance = env.balance(gw, XRP);
682
683 // bob can not mint on behalf of gw because he only has burn
684 // permission
685 env(pay(gw, alice, USD(50)),
686 delegate::as(bob),
688 env.close();
689 env.require(balance(bob, bobBalance - drops(baseFee)));
690 bobBalance = env.balance(bob, XRP);
691
692 // gw gives bob Payment permission as well
693 env(delegate::set(gw, bob, {"PaymentBurn", "Payment"}));
694 env.close();
695 env.require(balance(gw, gwBalance - drops(baseFee)));
696 gwBalance = env.balance(gw, XRP);
697
698 // bob now can mint on behalf of gw
699 env(pay(gw, alice, USD(50)), delegate::as(bob));
700 env.close();
701 env.require(balance(bob, bobBalance - drops(baseFee)));
702 env.require(balance(gw, gwBalance));
703 env.require(balance(alice, aliceBalance));
704 env.require(balance(gw, alice["USD"](-50)));
705 env.require(balance(alice, USD(50)));
706 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
707 }
708 }
709
710 void
712 {
713 testcase("test TrustSet granular permissions");
714 using namespace jtx;
715
716 // test TrustlineUnfreeze, TrustlineFreeze and TrustlineAuthorize
717 {
718 Env env(*this);
719 Account gw{"gw"};
720 Account alice{"alice"};
721 Account bob{"bob"};
722 env.fund(XRP(10000), gw, alice, bob);
723 env(fset(gw, asfRequireAuth));
724 env.close();
725
726 env(delegate::set(alice, bob, {"TrustlineUnfreeze"}));
727 env.close();
728 // bob can not create trustline on behalf of alice because he only
729 // has unfreeze permission
730 env(trust(alice, gw["USD"](50)),
731 delegate::as(bob),
733 env.close();
734
735 // alice creates trustline by herself
736 env(trust(alice, gw["USD"](50)));
737 env.close();
738
739 // gw gives bob unfreeze permission
740 env(delegate::set(gw, bob, {"TrustlineUnfreeze"}));
741 env.close();
742
743 // unsupported flags
744 env(trust(alice, gw["USD"](50), tfSetNoRipple),
745 delegate::as(bob),
747 env(trust(alice, gw["USD"](50), tfClearNoRipple),
748 delegate::as(bob),
750 env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze),
751 delegate::as(bob),
753 env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze),
754 delegate::as(bob),
756 env.close();
757
758 // supported flags with wrong permission
759 env(trust(gw, gw["USD"](0), alice, tfSetfAuth),
760 delegate::as(bob),
762 env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
763 delegate::as(bob),
765 env.close();
766
767 env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
768 env.close();
769 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
770 delegate::as(bob),
772 env.close();
773 // although trustline authorize is granted, bob can not change the
774 // limit number
775 env(trust(gw, gw["USD"](50), alice, tfSetfAuth),
776 delegate::as(bob),
778 env.close();
779
780 // supported flags with correct permission
781 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::as(bob));
782 env.close();
783 env(delegate::set(
784 gw, bob, {"TrustlineAuthorize", "TrustlineFreeze"}));
785 env.close();
786 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
787 env.close();
788 env(delegate::set(
789 gw, bob, {"TrustlineAuthorize", "TrustlineUnfreeze"}));
790 env.close();
791 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
792 delegate::as(bob));
793 env.close();
794 // but bob can not freeze trustline because he no longer has freeze
795 // permission
796 env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
797 delegate::as(bob),
799
800 // cannot update LimitAmount with granular permission, both high and
801 // low account
802 env(trust(alice, gw["USD"](100)),
803 delegate::as(bob),
805 env(trust(gw, alice["USD"](100)),
806 delegate::as(bob),
808
809 // can not set QualityIn or QualityOut
810 auto tx = trust(alice, gw["USD"](50));
811 tx["QualityIn"] = "1000";
812 env(tx, delegate::as(bob), ter(tecNO_PERMISSION));
813 auto tx2 = trust(alice, gw["USD"](50));
814 tx2["QualityOut"] = "1000";
815 env(tx2, delegate::as(bob), ter(tecNO_PERMISSION));
816 auto tx3 = trust(gw, alice["USD"](50));
817 tx3["QualityIn"] = "1000";
818 env(tx3, delegate::as(bob), ter(tecNO_PERMISSION));
819 auto tx4 = trust(gw, alice["USD"](50));
820 tx4["QualityOut"] = "1000";
821 env(tx4, delegate::as(bob), ter(tecNO_PERMISSION));
822
823 // granting TrustSet can make it work
824 env(delegate::set(gw, bob, {"TrustSet"}));
825 env.close();
826 auto tx5 = trust(gw, alice["USD"](50));
827 tx5["QualityOut"] = "1000";
828 env(tx5, delegate::as(bob));
829 auto tx6 = trust(alice, gw["USD"](50));
830 tx6["QualityOut"] = "1000";
831 env(tx6, delegate::as(bob), ter(tecNO_PERMISSION));
832 env(delegate::set(alice, bob, {"TrustSet"}));
833 env.close();
834 env(tx6, delegate::as(bob));
835 }
836
837 // test mix of transaction level delegation and granular delegation
838 {
839 Env env(*this);
840 Account gw{"gw"};
841 Account alice{"alice"};
842 Account bob{"bob"};
843 env.fund(XRP(10000), gw, alice, bob);
844 env(fset(gw, asfRequireAuth));
845 env.close();
846
847 // bob does not have permission
848 env(trust(alice, gw["USD"](50)),
849 delegate::as(bob),
851 env(delegate::set(
852 alice, bob, {"TrustlineUnfreeze", "NFTokenCreateOffer"}));
853 env.close();
854 // bob still does not have permission
855 env(trust(alice, gw["USD"](50)),
856 delegate::as(bob),
858
859 // add TrustSet permission and some unrelated permission
860 env(delegate::set(
861 alice,
862 bob,
863 {"TrustlineUnfreeze",
864 "NFTokenCreateOffer",
865 "TrustSet",
866 "AccountTransferRateSet"}));
867 env.close();
868 env(trust(alice, gw["USD"](50)), delegate::as(bob));
869 env.close();
870
871 env(delegate::set(
872 gw,
873 bob,
874 {"TrustlineUnfreeze",
875 "NFTokenCreateOffer",
876 "TrustSet",
877 "AccountTransferRateSet"}));
878 env.close();
879
880 // since bob has TrustSet permission, he does not need
881 // TrustlineFreeze granular permission to freeze the trustline
882 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
883 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
884 delegate::as(bob));
885 // bob can perform all the operations regarding TrustSet
886 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
887 env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze),
888 delegate::as(bob));
889 env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze),
890 delegate::as(bob));
891 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::as(bob));
892 env(trust(alice, gw["USD"](50), tfSetNoRipple), delegate::as(bob));
893 env(trust(alice, gw["USD"](50), tfClearNoRipple),
894 delegate::as(bob));
895 }
896 }
897
898 void
900 {
901 testcase("test AccountSet granular permissions");
902 using namespace jtx;
903
904 // test AccountDomainSet, AccountEmailHashSet,
905 // AccountMessageKeySet,AccountTransferRateSet, and AccountTickSizeSet
906 // granular permissions
907 {
908 Env env(*this);
909 auto const alice = Account{"alice"};
910 auto const bob = Account{"bob"};
911 env.fund(XRP(10000), alice, bob);
912 env.close();
913
914 // alice gives bob some random permission, which is not related to
915 // the AccountSet transaction
916 env(delegate::set(alice, bob, {"TrustlineUnfreeze"}));
917 env.close();
918
919 // bob does not have permission to set domain
920 // on behalf of alice
921 std::string const domain = "example.com";
922 auto jt = noop(alice);
923 jt[sfDomain.fieldName] = strHex(domain);
924 jt[sfDelegate.fieldName] = bob.human();
925 jt[sfFlags.fieldName] = tfFullyCanonicalSig;
926
927 // add granular permission related to AccountSet but is not the
928 // correct permission for domain set
929 env(delegate::set(
930 alice, bob, {"TrustlineUnfreeze", "AccountEmailHashSet"}));
931 env.close();
932 env(jt, ter(tecNO_PERMISSION));
933
934 // alice give granular permission of AccountDomainSet to bob
935 env(delegate::set(alice, bob, {"AccountDomainSet"}));
936 env.close();
937
938 // bob set account domain on behalf of alice
939 env(jt);
940 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
941
942 // bob can reset domain
943 jt[sfDomain.fieldName] = "";
944 env(jt);
945 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfDomain));
946
947 // if flag is not equal to tfFullyCanonicalSig, which means bob
948 // is trying to set the flag at the same time, it will fail
949 std::string const failDomain = "fail_domain_update";
950 jt[sfFlags.fieldName] = tfRequireAuth;
951 jt[sfDomain.fieldName] = strHex(failDomain);
952 env(jt, ter(tecNO_PERMISSION));
953 // reset flag number
954 jt[sfFlags.fieldName] = tfFullyCanonicalSig;
955
956 // bob tries to update domain and set email hash,
957 // but he does not have permission to set email hash
958 jt[sfDomain.fieldName] = strHex(domain);
959 std::string const mh("5F31A79367DC3137FADA860C05742EE6");
960 jt[sfEmailHash.fieldName] = mh;
961 env(jt, ter(tecNO_PERMISSION));
962
963 // alice give granular permission of AccountEmailHashSet to bob
964 env(delegate::set(
965 alice, bob, {"AccountDomainSet", "AccountEmailHashSet"}));
966 env.close();
967 env(jt);
968 BEAST_EXPECT(to_string((*env.le(alice))[sfEmailHash]) == mh);
969 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
970
971 // bob does not have permission to set message key for alice
972 auto const rkp = randomKeyPair(KeyType::ed25519);
973 jt[sfMessageKey.fieldName] = strHex(rkp.first.slice());
974 env(jt, ter(tecNO_PERMISSION));
975
976 // alice give granular permission of AccountMessageKeySet to bob
977 env(delegate::set(
978 alice,
979 bob,
980 {"AccountDomainSet",
981 "AccountEmailHashSet",
982 "AccountMessageKeySet"}));
983 env.close();
984
985 // bob can set message key for alice
986 env(jt);
987 BEAST_EXPECT(
988 strHex((*env.le(alice))[sfMessageKey]) ==
989 strHex(rkp.first.slice()));
990 jt[sfMessageKey.fieldName] = "";
991 env(jt);
992 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfMessageKey));
993
994 // bob does not have permission to set transfer rate for alice
995 env(rate(alice, 2.0), delegate::as(bob), ter(tecNO_PERMISSION));
996
997 // alice give granular permission of AccountTransferRateSet to bob
998 env(delegate::set(
999 alice,
1000 bob,
1001 {"AccountDomainSet",
1002 "AccountEmailHashSet",
1003 "AccountMessageKeySet",
1004 "AccountTransferRateSet"}));
1005 env.close();
1006 auto jtRate = rate(alice, 2.0);
1007 jtRate[sfDelegate.fieldName] = bob.human();
1008 jtRate[sfFlags.fieldName] = tfFullyCanonicalSig;
1009 env(jtRate, delegate::as(bob));
1010 BEAST_EXPECT((*env.le(alice))[sfTransferRate] == 2000000000);
1011
1012 // bob does not have permission to set ticksize for alice
1013 jt[sfTickSize.fieldName] = 8;
1014 env(jt, ter(tecNO_PERMISSION));
1015
1016 // alice give granular permission of AccountTickSizeSet to bob
1017 env(delegate::set(
1018 alice,
1019 bob,
1020 {"AccountDomainSet",
1021 "AccountEmailHashSet",
1022 "AccountMessageKeySet",
1023 "AccountTransferRateSet",
1024 "AccountTickSizeSet"}));
1025 env.close();
1026 env(jt);
1027 BEAST_EXPECT((*env.le(alice))[sfTickSize] == 8);
1028
1029 // can not set asfRequireAuth flag for alice
1030 env(fset(alice, asfRequireAuth),
1031 delegate::as(bob),
1033
1034 // reset Delegate will delete the Delegate
1035 // object
1036 env(delegate::set(alice, bob, {}));
1037 // bib still does not have permission to set asfRequireAuth for
1038 // alice
1039 env(fset(alice, asfRequireAuth),
1040 delegate::as(bob),
1042 // alice can set for herself
1043 env(fset(alice, asfRequireAuth));
1044 env.require(flags(alice, asfRequireAuth));
1045 env.close();
1046
1047 // can not update tick size because bob no longer has permission
1048 jt[sfTickSize.fieldName] = 7;
1049 env(jt, ter(tecNO_PERMISSION));
1050
1051 env(delegate::set(
1052 alice,
1053 bob,
1054 {"AccountDomainSet",
1055 "AccountEmailHashSet",
1056 "AccountMessageKeySet"}));
1057 env.close();
1058
1059 // bob does not have permission to set wallet locater for alice
1060 std::string const locator =
1061 "9633EC8AF54F16B5286DB1D7B519EF49EEFC050C0C8AC4384F1D88ACD1BFDF"
1062 "05";
1063 auto jt2 = noop(alice);
1064 jt2[sfDomain.fieldName] = strHex(domain);
1065 jt2[sfDelegate.fieldName] = bob.human();
1066 jt2[sfWalletLocator.fieldName] = locator;
1067 jt2[sfFlags.fieldName] = tfFullyCanonicalSig;
1068 env(jt2, ter(tecNO_PERMISSION));
1069 }
1070
1071 // can not set AccountSet flags on behalf of other account
1072 {
1073 Env env(*this);
1074 auto const alice = Account{"alice"};
1075 auto const bob = Account{"bob"};
1076 env.fund(XRP(10000), alice, bob);
1077 env.close();
1078
1079 auto testSetClearFlag = [&](std::uint32_t flag) {
1080 // bob can not set flag on behalf of alice
1081 env(fset(alice, flag),
1082 delegate::as(bob),
1084 // alice set by herself
1085 env(fset(alice, flag));
1086 env.close();
1087 env.require(flags(alice, flag));
1088 // bob can not clear on behalf of alice
1089 env(fclear(alice, flag),
1090 delegate::as(bob),
1092 };
1093
1094 // testSetClearFlag(asfNoFreeze);
1095 testSetClearFlag(asfRequireAuth);
1096 testSetClearFlag(asfAllowTrustLineClawback);
1097
1098 // alice gives some granular permissions to bob
1099 env(delegate::set(
1100 alice,
1101 bob,
1102 {"AccountDomainSet",
1103 "AccountEmailHashSet",
1104 "AccountMessageKeySet"}));
1105 env.close();
1106
1107 testSetClearFlag(asfDefaultRipple);
1108 testSetClearFlag(asfDepositAuth);
1109 testSetClearFlag(asfDisallowIncomingCheck);
1110 testSetClearFlag(asfDisallowIncomingNFTokenOffer);
1111 testSetClearFlag(asfDisallowIncomingPayChan);
1112 testSetClearFlag(asfDisallowIncomingTrustline);
1113 testSetClearFlag(asfDisallowXRP);
1114 testSetClearFlag(asfRequireDest);
1115 testSetClearFlag(asfGlobalFreeze);
1116
1117 // bob can not set asfAccountTxnID on behalf of alice
1118 env(fset(alice, asfAccountTxnID),
1119 delegate::as(bob),
1121 env(fset(alice, asfAccountTxnID));
1122 env.close();
1123 BEAST_EXPECT(env.le(alice)->isFieldPresent(sfAccountTxnID));
1124 env(fclear(alice, asfAccountTxnID),
1125 delegate::as(bob),
1127
1128 // bob can not set asfAuthorizedNFTokenMinter on behalf of alice
1130 jt[sfDelegate.fieldName] = bob.human();
1131 jt[sfNFTokenMinter.fieldName] = bob.human();
1132 env(jt, ter(tecNO_PERMISSION));
1133
1134 // bob gives alice some permissions
1135 env(delegate::set(
1136 bob,
1137 alice,
1138 {"AccountDomainSet",
1139 "AccountEmailHashSet",
1140 "AccountMessageKeySet"}));
1141 env.close();
1142
1143 // since we can not set asfNoFreeze if asfAllowTrustLineClawback is
1144 // set, which can not be clear either. Test alice set asfNoFreeze on
1145 // behalf of bob.
1146 env(fset(alice, asfNoFreeze),
1147 delegate::as(bob),
1149 env(fset(bob, asfNoFreeze));
1150 env.close();
1151 env.require(flags(bob, asfNoFreeze));
1152 // alice can not clear on behalf of bob
1153 env(fclear(alice, asfNoFreeze),
1154 delegate::as(bob),
1156
1157 // bob can not set asfDisableMaster on behalf of alice
1158 Account const bobKey{"bobKey", KeyType::secp256k1};
1159 env(regkey(bob, bobKey));
1160 env.close();
1161 env(fset(alice, asfDisableMaster),
1162 delegate::as(bob),
1163 sig(bob),
1165 }
1166 }
1167
1168 void
1170 {
1171 testcase("test MPTokenIssuanceSet granular");
1172 using namespace jtx;
1173
1174 // test MPTokenIssuanceUnlock and MPTokenIssuanceLock permissions
1175 {
1176 Env env(*this);
1177 Account alice{"alice"};
1178 Account bob{"bob"};
1179 env.fund(XRP(100000), alice, bob);
1180 env.close();
1181
1182 MPTTester mpt(env, alice, {.fund = false});
1183 env.close();
1184 mpt.create({.flags = tfMPTCanLock});
1185 env.close();
1186
1187 // delegate ledger object is not created yet
1188 mpt.set(
1189 {.account = alice,
1190 .flags = tfMPTLock,
1191 .delegate = bob,
1192 .err = tecNO_PERMISSION});
1193
1194 // alice gives granular permission to bob of MPTokenIssuanceUnlock
1195 env(delegate::set(alice, bob, {"MPTokenIssuanceUnlock"}));
1196 env.close();
1197 // bob does not have lock permission
1198 mpt.set(
1199 {.account = alice,
1200 .flags = tfMPTLock,
1201 .delegate = bob,
1202 .err = tecNO_PERMISSION});
1203 // bob now has lock permission, but does not have unlock permission
1204 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
1205 env.close();
1206 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1207 mpt.set(
1208 {.account = alice,
1209 .flags = tfMPTUnlock,
1210 .delegate = bob,
1211 .err = tecNO_PERMISSION});
1212
1213 // now bob can lock and unlock
1214 env(delegate::set(
1215 alice, bob, {"MPTokenIssuanceLock", "MPTokenIssuanceUnlock"}));
1216 env.close();
1217 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1218 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1219 env.close();
1220 }
1221
1222 // test mix of granular and transaction level permission
1223 {
1224 Env env(*this);
1225 Account alice{"alice"};
1226 Account bob{"bob"};
1227 env.fund(XRP(100000), alice, bob);
1228 env.close();
1229
1230 MPTTester mpt(env, alice, {.fund = false});
1231 env.close();
1232 mpt.create({.flags = tfMPTCanLock});
1233 env.close();
1234
1235 // alice gives granular permission to bob of MPTokenIssuanceLock
1236 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
1237 env.close();
1238 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1239 // bob does not have unlock permission
1240 mpt.set(
1241 {.account = alice,
1242 .flags = tfMPTUnlock,
1243 .delegate = bob,
1244 .err = tecNO_PERMISSION});
1245
1246 // alice gives bob some unrelated permission with
1247 // MPTokenIssuanceLock
1248 env(delegate::set(
1249 alice,
1250 bob,
1251 {"NFTokenMint", "MPTokenIssuanceLock", "NFTokenBurn"}));
1252 env.close();
1253 // bob can not unlock
1254 mpt.set(
1255 {.account = alice,
1256 .flags = tfMPTUnlock,
1257 .delegate = bob,
1258 .err = tecNO_PERMISSION});
1259
1260 // alice add MPTokenIssuanceSet to permissions
1261 env(delegate::set(
1262 alice,
1263 bob,
1264 {"NFTokenMint",
1265 "MPTokenIssuanceLock",
1266 "NFTokenBurn",
1267 "MPTokenIssuanceSet"}));
1268 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1269 // alice can lock by herself
1270 mpt.set({.account = alice, .flags = tfMPTLock});
1271 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1272 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1273 }
1274 }
1275
1276 void
1278 {
1279 testcase("test single sign");
1280 using namespace jtx;
1281
1282 Env env(*this);
1283 Account alice{"alice"};
1284 Account bob{"bob"};
1285 Account carol{"carol"};
1286 env.fund(XRP(100000), alice, bob, carol);
1287 env.close();
1288
1289 env(delegate::set(alice, bob, {"Payment"}));
1290 env.close();
1291
1292 auto aliceBalance = env.balance(alice);
1293 auto bobBalance = env.balance(bob);
1294 auto carolBalance = env.balance(carol);
1295
1296 env(pay(alice, carol, XRP(100)),
1297 fee(XRP(10)),
1298 delegate::as(bob),
1299 sig(bob));
1300 env.close();
1301 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100));
1302 BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10));
1303 BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(100));
1304 }
1305
1306 void
1308 {
1309 testcase("test single sign with bad secret");
1310 using namespace jtx;
1311
1312 Env env(*this);
1313 Account alice{"alice"};
1314 Account bob{"bob"};
1315 Account carol{"carol"};
1316 env.fund(XRP(100000), alice, bob, carol);
1317 env.close();
1318
1319 env(delegate::set(alice, bob, {"Payment"}));
1320 env.close();
1321
1322 auto aliceBalance = env.balance(alice);
1323 auto bobBalance = env.balance(bob);
1324 auto carolBalance = env.balance(carol);
1325
1326 env(pay(alice, carol, XRP(100)),
1327 fee(XRP(10)),
1328 delegate::as(bob),
1329 sig(alice),
1330 ter(tefBAD_AUTH));
1331 env.close();
1332 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1333 BEAST_EXPECT(env.balance(bob) == bobBalance);
1334 BEAST_EXPECT(env.balance(carol) == carolBalance);
1335 }
1336
1337 void
1339 {
1340 testcase("test multi sign");
1341 using namespace jtx;
1342
1343 Env env(*this);
1344 Account alice{"alice"};
1345 Account bob{"bob"};
1346 Account carol{"carol"};
1347 Account daria{"daria"};
1348 Account edward{"edward"};
1349 env.fund(XRP(100000), alice, bob, carol, daria, edward);
1350 env.close();
1351
1352 env(signers(bob, 2, {{daria, 1}, {edward, 1}}));
1353 env.close();
1354
1355 env(delegate::set(alice, bob, {"Payment"}));
1356 env.close();
1357
1358 auto aliceBalance = env.balance(alice);
1359 auto bobBalance = env.balance(bob);
1360 auto carolBalance = env.balance(carol);
1361 auto dariaBalance = env.balance(daria);
1362 auto edwardBalance = env.balance(edward);
1363
1364 env(pay(alice, carol, XRP(100)),
1365 fee(XRP(10)),
1366 delegate::as(bob),
1367 msig(daria, edward));
1368 env.close();
1369 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100));
1370 BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10));
1371 BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(100));
1372 BEAST_EXPECT(env.balance(daria) == dariaBalance);
1373 BEAST_EXPECT(env.balance(edward) == edwardBalance);
1374 }
1375
1376 void
1378 {
1379 testcase("test multi sign which does not meet quorum");
1380 using namespace jtx;
1381
1382 Env env(*this);
1383 Account alice{"alice"};
1384 Account bob{"bob"};
1385 Account carol{"carol"};
1386 Account daria = Account{"daria"};
1387 Account edward = Account{"edward"};
1388 Account fred = Account{"fred"};
1389 env.fund(XRP(100000), alice, bob, carol, daria, edward, fred);
1390 env.close();
1391
1392 env(signers(bob, 3, {{daria, 1}, {edward, 1}, {fred, 1}}));
1393 env.close();
1394
1395 env(delegate::set(alice, bob, {"Payment"}));
1396 env.close();
1397
1398 auto aliceBalance = env.balance(alice);
1399 auto bobBalance = env.balance(bob);
1400 auto carolBalance = env.balance(carol);
1401 auto dariaBalance = env.balance(daria);
1402 auto edwardBalance = env.balance(edward);
1403
1404 env(pay(alice, carol, XRP(100)),
1405 fee(XRP(10)),
1406 delegate::as(bob),
1407 msig(daria, edward),
1409 env.close();
1410 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1411 BEAST_EXPECT(env.balance(bob) == bobBalance);
1412 BEAST_EXPECT(env.balance(carol) == carolBalance);
1413 BEAST_EXPECT(env.balance(daria) == dariaBalance);
1414 BEAST_EXPECT(env.balance(edward) == edwardBalance);
1415 }
1416
1417 void
1418 run() override
1419 {
1423 testReserve();
1424 testFee();
1425 testSequence();
1434 testMultiSign();
1436 }
1437};
1438BEAST_DEFINE_TESTSUITE(Delegate, app, ripple);
1439} // namespace test
1440} // namespace ripple
Represents a JSON value.
Definition: json_value.h:150
Value & append(Value const &value)
Append value to array at the end.
Definition: json_value.cpp:910
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
void run() override
Runs the suite.
Immutable cryptographic account descriptor.
Definition: Account.h:39
A transaction testing environment.
Definition: Env.h:121
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition: Env.cpp:111
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition: Env.cpp:212
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:535
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:331
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:117
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:264
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:233
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition: Env.cpp:179
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:221
A balance matches.
Definition: balance.h:39
Sets the optional URI on a DIDSet.
Definition: did.h:60
Set the fee on a JTx.
Definition: fee.h:37
Match set account flags.
Definition: flags.h:125
Set a multisignature on a JTx.
Definition: multisign.h:67
Set the regular signature on a JTx.
Definition: sig.h:35
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
@ arrayValue
array value (ordered list)
Definition: json_value.h:45
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Definition: Indexes.cpp:458
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:177
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:367
Json::Value create(A const &account, A const &dest, STAmount const &sendMax)
Create a check.
Definition: TestHelpers.h:450
Json::Value entry(jtx::Env &env, jtx::Account const &account, jtx::Account const &authorize)
Definition: delegate.cpp:55
Json::Value set(jtx::Account const &account, jtx::Account const &authorize, std::vector< std::string > const &permissions)
Definition: delegate.cpp:31
Json::Value set(jtx::Account const &account)
Definition: dids.cpp:33
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition: flags.h:41
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition: regkey.cpp:29
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition: multisign.cpp:34
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:32
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition: flags.cpp:29
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:30
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition: rate.cpp:32
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
Definition: acctdelete.cpp:31
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
FeatureBitset supported_amendments()
Definition: Env.h:74
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
constexpr std::uint32_t asfGlobalFreeze
Definition: TxFlags.h:83
constexpr std::uint32_t asfDepositAuth
Definition: TxFlags.h:85
constexpr std::uint32_t asfDisallowIncomingNFTokenOffer
Definition: TxFlags.h:90
constexpr std::uint32_t tfSetDeepFreeze
Definition: TxFlags.h:119
constexpr std::uint32_t asfRequireDest
Definition: TxFlags.h:77
constexpr std::uint32_t const tfMPTUnlock
Definition: TxFlags.h:165
constexpr std::uint32_t asfAuthorizedNFTokenMinter
Definition: TxFlags.h:86
constexpr std::uint32_t asfNoFreeze
Definition: TxFlags.h:82
constexpr std::uint32_t asfDisableMaster
Definition: TxFlags.h:80
constexpr std::uint32_t asfDisallowIncomingTrustline
Definition: TxFlags.h:93
@ tefBAD_AUTH
Definition: TER.h:169
@ tefBAD_QUORUM
Definition: TER.h:180
constexpr std::uint32_t tfClearNoRipple
Definition: TxFlags.h:116
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
constexpr std::uint32_t tfSetfAuth
Definition: TxFlags.h:114
constexpr std::uint32_t asfAccountTxnID
Definition: TxFlags.h:81
constexpr std::uint32_t asfDefaultRipple
Definition: TxFlags.h:84
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:244
constexpr std::uint32_t asfDisallowIncomingCheck
Definition: TxFlags.h:91
constexpr std::uint32_t tfClearFreeze
Definition: TxFlags.h:118
constexpr std::uint32_t tfRequireAuth
Definition: TxFlags.h:68
@ tecNO_PERMISSION
Definition: TER.h:305
@ tecUNFUNDED_PAYMENT
Definition: TER.h:285
@ tecINSUFFICIENT_RESERVE
Definition: TER.h:307
constexpr std::uint32_t const tfMPTLock
Definition: TxFlags.h:164
constexpr std::uint32_t tfClearDeepFreeze
Definition: TxFlags.h:120
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
constexpr std::uint32_t asfDisallowIncomingPayChan
Definition: TxFlags.h:92
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
Definition: SecretKey.cpp:386
constexpr std::uint32_t tfFullyCanonicalSig
Transaction flags.
Definition: TxFlags.h:60
constexpr std::uint32_t asfAllowTrustLineClawback
Definition: TxFlags.h:94
constexpr std::uint32_t asfRequireAuth
Definition: TxFlags.h:78
@ terINSUF_FEE_B
Definition: TER.h:216
@ terNO_ACCOUNT
Definition: TER.h:217
constexpr std::uint32_t tfSetFreeze
Definition: TxFlags.h:117
constexpr std::uint32_t tfSetNoRipple
Definition: TxFlags.h:115
constexpr std::uint32_t const tfMPTCanLock
Definition: TxFlags.h:150
constexpr std::uint32_t asfDisallowXRP
Definition: TxFlags.h:79
@ temREDUNDANT
Definition: TER.h:112
@ temBAD_FEE
Definition: TER.h:92
@ temBAD_SIGNER
Definition: TER.h:115
@ temMALFORMED
Definition: TER.h:87
@ temARRAY_TOO_LARGE
Definition: TER.h:141
@ temDISABLED
Definition: TER.h:114