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 }
235 }
236
237 void
239 {
240 testcase("test reserve");
241 using namespace jtx;
242
243 // test reserve for DelegateSet
244 {
245 Env env(*this);
246 Account alice{"alice"};
247 Account bob{"bob"};
248 Account carol{"carol"};
249
250 env.fund(drops(env.current()->fees().accountReserve(0)), alice);
251 env.fund(
252 drops(env.current()->fees().accountReserve(1)), bob, carol);
253 env.close();
254
255 // alice does not have enough reserve to create Delegate
256 env(delegate::set(alice, bob, {"Payment"}),
258
259 // bob has enough reserve
260 env(delegate::set(bob, alice, {"Payment"}));
261 env.close();
262
263 // now bob create another Delegate, he does not have
264 // enough reserve
265 env(delegate::set(bob, carol, {"Payment"}),
267 }
268
269 // test reserve when sending transaction on behalf of other account
270 {
271 Env env(*this);
272 Account alice{"alice"};
273 Account bob{"bob"};
274
275 env.fund(drops(env.current()->fees().accountReserve(1)), alice);
276 env.fund(drops(env.current()->fees().accountReserve(2)), bob);
277 env.close();
278
279 // alice gives bob permission
280 env(delegate::set(alice, bob, {"DIDSet", "DIDDelete"}));
281 env.close();
282
283 // bob set DID on behalf of alice, but alice does not have enough
284 // reserve
285 env(did::set(alice),
286 did::uri("uri"),
287 delegate::as(bob),
289
290 // bob can set DID for himself because he has enough reserve
291 env(did::set(bob), did::uri("uri"));
292 env.close();
293 }
294 }
295
296 void
298 {
299 testcase("test fee");
300 using namespace jtx;
301
302 Env env(*this);
303 Account alice{"alice"};
304 Account bob{"bob"};
305 Account carol{"carol"};
306 env.fund(XRP(10000), alice, carol);
307 env.fund(XRP(1000), bob);
308 env.close();
309
310 {
311 // Fee should be checked before permission check,
312 // otherwise tecNO_PERMISSION returned when permission check fails
313 // could cause context reset to pay fee because it is tec error
314 auto aliceBalance = env.balance(alice);
315 auto bobBalance = env.balance(bob);
316 auto carolBalance = env.balance(carol);
317
318 env(pay(alice, carol, XRP(100)),
319 fee(XRP(2000)),
320 delegate::as(bob),
322 env.close();
323 BEAST_EXPECT(env.balance(alice) == aliceBalance);
324 BEAST_EXPECT(env.balance(bob) == bobBalance);
325 BEAST_EXPECT(env.balance(carol) == carolBalance);
326 }
327
328 env(delegate::set(alice, bob, {"Payment"}));
329 env.close();
330
331 {
332 // Delegate pays the fee
333 auto aliceBalance = env.balance(alice);
334 auto bobBalance = env.balance(bob);
335 auto carolBalance = env.balance(carol);
336
337 auto const sendAmt = XRP(100);
338 auto const feeAmt = XRP(10);
339 env(pay(alice, carol, sendAmt), fee(feeAmt), delegate::as(bob));
340 env.close();
341 BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt);
342 BEAST_EXPECT(env.balance(bob) == bobBalance - feeAmt);
343 BEAST_EXPECT(env.balance(carol) == carolBalance + sendAmt);
344 }
345
346 {
347 // insufficient balance to pay fee
348 auto aliceBalance = env.balance(alice);
349 auto bobBalance = env.balance(bob);
350 auto carolBalance = env.balance(carol);
351
352 env(pay(alice, carol, XRP(100)),
353 fee(XRP(2000)),
354 delegate::as(bob),
356 env.close();
357 BEAST_EXPECT(env.balance(alice) == aliceBalance);
358 BEAST_EXPECT(env.balance(bob) == bobBalance);
359 BEAST_EXPECT(env.balance(carol) == carolBalance);
360 }
361
362 {
363 // fee is paid by Delegate
364 // on context reset (tec error)
365 auto aliceBalance = env.balance(alice);
366 auto bobBalance = env.balance(bob);
367 auto carolBalance = env.balance(carol);
368 auto const feeAmt = XRP(10);
369
370 env(pay(alice, carol, XRP(20000)),
371 fee(feeAmt),
372 delegate::as(bob),
374 env.close();
375 BEAST_EXPECT(env.balance(alice) == aliceBalance);
376 BEAST_EXPECT(env.balance(bob) == bobBalance - feeAmt);
377 BEAST_EXPECT(env.balance(carol) == carolBalance);
378 }
379 }
380
381 void
383 {
384 testcase("test sequence");
385 using namespace jtx;
386
387 Env env(*this);
388 Account alice{"alice"};
389 Account bob{"bob"};
390 Account carol{"carol"};
391 env.fund(XRP(10000), alice, bob, carol);
392 env.close();
393
394 auto aliceSeq = env.seq(alice);
395 auto bobSeq = env.seq(bob);
396 env(delegate::set(alice, bob, {"Payment"}));
397 env(delegate::set(bob, alice, {"Payment"}));
398 env.close();
399 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
400 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
401 aliceSeq = env.seq(alice);
402 bobSeq = env.seq(bob);
403
404 for (auto i = 0; i < 20; ++i)
405 {
406 // bob is the delegated account, his sequence won't increment
407 env(pay(alice, carol, XRP(10)), fee(XRP(10)), delegate::as(bob));
408 env.close();
409 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
410 BEAST_EXPECT(env.seq(bob) == bobSeq);
411 aliceSeq = env.seq(alice);
412
413 // bob sends payment for himself, his sequence will increment
414 env(pay(bob, carol, XRP(10)), fee(XRP(10)));
415 BEAST_EXPECT(env.seq(alice) == aliceSeq);
416 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
417 bobSeq = env.seq(bob);
418
419 // alice is the delegated account, her sequence won't increment
420 env(pay(bob, carol, XRP(10)), fee(XRP(10)), delegate::as(alice));
421 env.close();
422 BEAST_EXPECT(env.seq(alice) == aliceSeq);
423 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
424 bobSeq = env.seq(bob);
425
426 // alice sends payment for herself, her sequence will increment
427 env(pay(alice, carol, XRP(10)), fee(XRP(10)));
428 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
429 BEAST_EXPECT(env.seq(bob) == bobSeq);
430 aliceSeq = env.seq(alice);
431 }
432 }
433
434 void
436 {
437 testcase("test deleting account");
438 using namespace jtx;
439
440 Env env(*this);
441 Account alice{"alice"};
442 Account bob{"bob"};
443 env.fund(XRP(100000), alice, bob);
444 env.close();
445
446 env(delegate::set(alice, bob, {"Payment"}));
447 env.close();
448 BEAST_EXPECT(
449 env.closed()->exists(keylet::delegate(alice.id(), bob.id())));
450
451 for (std::uint32_t i = 0; i < 256; ++i)
452 env.close();
453
454 auto const aliceBalance = env.balance(alice);
455 auto const bobBalance = env.balance(bob);
456
457 // alice deletes account, this will remove the Delegate object
458 auto const deleteFee = drops(env.current()->fees().increment);
459 env(acctdelete(alice, bob), fee(deleteFee));
460 env.close();
461
462 BEAST_EXPECT(!env.closed()->exists(keylet::account(alice.id())));
463 BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(alice.id())));
464 BEAST_EXPECT(env.balance(bob) == bobBalance + aliceBalance - deleteFee);
465
466 BEAST_EXPECT(
467 !env.closed()->exists(keylet::delegate(alice.id(), bob.id())));
468 }
469
470 void
472 {
473 testcase("test delegate transaction");
474 using namespace jtx;
475
476 Env env(*this);
477 Account alice{"alice"};
478 Account bob{"bob"};
479 Account carol{"carol"};
480
481 XRPAmount const baseFee{env.current()->fees().base};
482
483 // use different initial amount to distinguish the source balance
484 env.fund(XRP(10000), alice);
485 env.fund(XRP(20000), bob);
486 env.fund(XRP(30000), carol);
487 env.close();
488
489 auto aliceBalance = env.balance(alice, XRP);
490 auto bobBalance = env.balance(bob, XRP);
491 auto carolBalance = env.balance(carol, XRP);
492
493 // can not send transaction on one's own behalf
494 env(pay(alice, bob, XRP(50)), delegate::as(alice), ter(temBAD_SIGNER));
495 env.require(balance(alice, aliceBalance));
496
497 env(delegate::set(alice, bob, {"Payment"}));
498 env.close();
499 env.require(balance(alice, aliceBalance - drops(baseFee)));
500 aliceBalance = env.balance(alice, XRP);
501
502 // bob pays 50 XRP to carol on behalf of alice
503 env(pay(alice, carol, XRP(50)), delegate::as(bob));
504 env.close();
505 env.require(balance(alice, aliceBalance - XRP(50)));
506 env.require(balance(carol, carolBalance + XRP(50)));
507 // bob pays the fee
508 env.require(balance(bob, bobBalance - drops(baseFee)));
509 aliceBalance = env.balance(alice, XRP);
510 bobBalance = env.balance(bob, XRP);
511 carolBalance = env.balance(carol, XRP);
512
513 // bob pays 50 XRP to bob self on behalf of alice
514 env(pay(alice, bob, XRP(50)), delegate::as(bob));
515 env.close();
516 env.require(balance(alice, aliceBalance - XRP(50)));
517 env.require(balance(bob, bobBalance + XRP(50) - drops(baseFee)));
518 aliceBalance = env.balance(alice, XRP);
519 bobBalance = env.balance(bob, XRP);
520
521 // bob pay 50 XRP to alice herself on behalf of alice
522 env(pay(alice, alice, XRP(50)), delegate::as(bob), ter(temREDUNDANT));
523 env.close();
524
525 // bob does not have permission to create check
526 env(check::create(alice, bob, XRP(10)),
527 delegate::as(bob),
529
530 // carol does not have permission to create check
531 env(check::create(alice, bob, XRP(10)),
532 delegate::as(carol),
534 }
535
536 void
538 {
539 testcase("test payment granular");
540 using namespace jtx;
541
542 // test PaymentMint and PaymentBurn
543 {
544 Env env(*this);
545 Account alice{"alice"};
546 Account bob{"bob"};
547 Account gw{"gateway"};
548 Account gw2{"gateway2"};
549 auto const USD = gw["USD"];
550 auto const EUR = gw2["EUR"];
551
552 env.fund(XRP(10000), alice);
553 env.fund(XRP(20000), bob);
554 env.fund(XRP(40000), gw, gw2);
555 env.trust(USD(200), alice);
556 env.trust(EUR(400), gw);
557 env.close();
558
559 XRPAmount const baseFee{env.current()->fees().base};
560 auto aliceBalance = env.balance(alice, XRP);
561 auto bobBalance = env.balance(bob, XRP);
562 auto gwBalance = env.balance(gw, XRP);
563 auto gw2Balance = env.balance(gw2, XRP);
564
565 // delegate ledger object is not created yet
566 env(pay(gw, alice, USD(50)),
567 delegate::as(bob),
569 env.require(balance(bob, bobBalance - drops(baseFee)));
570 bobBalance = env.balance(bob, XRP);
571
572 // gw gives bob burn permission
573 env(delegate::set(gw, bob, {"PaymentBurn"}));
574 env.close();
575 env.require(balance(gw, gwBalance - drops(baseFee)));
576 gwBalance = env.balance(gw, XRP);
577
578 // bob sends a payment transaction on behalf of gw
579 env(pay(gw, alice, USD(50)),
580 delegate::as(bob),
582 env.close();
583 env.require(balance(bob, bobBalance - drops(baseFee)));
584 bobBalance = env.balance(bob, XRP);
585
586 // gw gives bob mint permission, alice gives bob burn permission
587 env(delegate::set(gw, bob, {"PaymentMint"}));
588 env(delegate::set(alice, bob, {"PaymentBurn"}));
589 env.close();
590 env.require(balance(alice, aliceBalance - drops(baseFee)));
591 env.require(balance(gw, gwBalance - drops(baseFee)));
592 aliceBalance = env.balance(alice, XRP);
593 gwBalance = env.balance(gw, XRP);
594
595 // can not send XRP
596 env(pay(gw, alice, XRP(50)),
597 delegate::as(bob),
599 env.close();
600 env.require(balance(bob, bobBalance - drops(baseFee)));
601 bobBalance = env.balance(bob, XRP);
602
603 // mint 50 USD
604 env(pay(gw, alice, USD(50)), delegate::as(bob));
605 env.close();
606 env.require(balance(bob, bobBalance - drops(baseFee)));
607 env.require(balance(gw, gwBalance));
608 env.require(balance(gw, alice["USD"](-50)));
609 env.require(balance(alice, USD(50)));
610 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
611 bobBalance = env.balance(bob, XRP);
612
613 // burn 30 USD
614 env(pay(alice, gw, USD(30)), delegate::as(bob));
615 env.close();
616 env.require(balance(bob, bobBalance - drops(baseFee)));
617 env.require(balance(gw, gwBalance));
618 env.require(balance(gw, alice["USD"](-20)));
619 env.require(balance(alice, USD(20)));
620 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
621 bobBalance = env.balance(bob, XRP);
622
623 // bob has both mint and burn permissions
624 env(delegate::set(gw, bob, {"PaymentMint", "PaymentBurn"}));
625 env.close();
626 env.require(balance(gw, gwBalance - drops(baseFee)));
627 gwBalance = env.balance(gw, XRP);
628
629 // mint 100 USD for gw
630 env(pay(gw, alice, USD(100)), delegate::as(bob));
631 env.close();
632 env.require(balance(gw, alice["USD"](-120)));
633 env.require(balance(alice, USD(120)));
634 env.require(balance(bob, bobBalance - drops(baseFee)));
635 bobBalance = env.balance(bob, XRP);
636
637 // gw2 pays gw 200 EUR
638 env(pay(gw2, gw, EUR(200)));
639 env.close();
640 env.require(balance(gw2, gw2Balance - drops(baseFee)));
641 gw2Balance = env.balance(gw2, XRP);
642 env.require(balance(gw2, gw["EUR"](-200)));
643 env.require(balance(gw, EUR(200)));
644
645 // burn 100 EUR for gw
646 env(pay(gw, gw2, EUR(100)), delegate::as(bob));
647 env.close();
648 env.require(balance(gw2, gw["EUR"](-100)));
649 env.require(balance(gw, EUR(100)));
650 env.require(balance(bob, bobBalance - drops(baseFee)));
651 env.require(balance(gw, gwBalance));
652 env.require(balance(gw2, gw2Balance));
653 env.require(balance(alice, aliceBalance));
654 }
655
656 // test PaymentMint won't affect Payment transaction level delegation.
657 {
658 Env env(*this);
659 Account alice{"alice"};
660 Account bob{"bob"};
661 Account gw{"gateway"};
662 auto const USD = gw["USD"];
663
664 env.fund(XRP(10000), alice);
665 env.fund(XRP(20000), bob);
666 env.fund(XRP(40000), gw);
667 env.trust(USD(200), alice);
668 env.close();
669
670 XRPAmount const baseFee{env.current()->fees().base};
671
672 auto aliceBalance = env.balance(alice, XRP);
673 auto bobBalance = env.balance(bob, XRP);
674 auto gwBalance = env.balance(gw, XRP);
675
676 // gw gives bob PaymentBurn permission
677 env(delegate::set(gw, bob, {"PaymentBurn"}));
678 env.close();
679 env.require(balance(gw, gwBalance - drops(baseFee)));
680 gwBalance = env.balance(gw, XRP);
681
682 // bob can not mint on behalf of gw because he only has burn
683 // permission
684 env(pay(gw, alice, USD(50)),
685 delegate::as(bob),
687 env.close();
688 env.require(balance(bob, bobBalance - drops(baseFee)));
689 bobBalance = env.balance(bob, XRP);
690
691 // gw gives bob Payment permission as well
692 env(delegate::set(gw, bob, {"PaymentBurn", "Payment"}));
693 env.close();
694 env.require(balance(gw, gwBalance - drops(baseFee)));
695 gwBalance = env.balance(gw, XRP);
696
697 // bob now can mint on behalf of gw
698 env(pay(gw, alice, USD(50)), delegate::as(bob));
699 env.close();
700 env.require(balance(bob, bobBalance - drops(baseFee)));
701 env.require(balance(gw, gwBalance));
702 env.require(balance(alice, aliceBalance));
703 env.require(balance(gw, alice["USD"](-50)));
704 env.require(balance(alice, USD(50)));
705 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
706 }
707 }
708
709 void
711 {
712 testcase("test TrustSet granular permissions");
713 using namespace jtx;
714
715 // test TrustlineUnfreeze, TrustlineFreeze and TrustlineAuthorize
716 {
717 Env env(*this);
718 Account gw{"gw"};
719 Account alice{"alice"};
720 Account bob{"bob"};
721 env.fund(XRP(10000), gw, alice, bob);
722 env(fset(gw, asfRequireAuth));
723 env.close();
724
725 env(delegate::set(alice, bob, {"TrustlineUnfreeze"}));
726 env.close();
727 // bob can not create trustline on behalf of alice because he only
728 // has unfreeze permission
729 env(trust(alice, gw["USD"](50)),
730 delegate::as(bob),
732 env.close();
733
734 // alice creates trustline by herself
735 env(trust(alice, gw["USD"](50)));
736 env.close();
737
738 // gw gives bob unfreeze permission
739 env(delegate::set(gw, bob, {"TrustlineUnfreeze"}));
740 env.close();
741
742 // unsupported flags
743 env(trust(alice, gw["USD"](50), tfSetNoRipple),
744 delegate::as(bob),
746 env(trust(alice, gw["USD"](50), tfClearNoRipple),
747 delegate::as(bob),
749 env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze),
750 delegate::as(bob),
752 env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze),
753 delegate::as(bob),
755 env.close();
756
757 // supported flags with wrong permission
758 env(trust(gw, gw["USD"](0), alice, tfSetfAuth),
759 delegate::as(bob),
761 env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
762 delegate::as(bob),
764 env.close();
765
766 env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
767 env.close();
768 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
769 delegate::as(bob),
771 env.close();
772 // although trustline authorize is granted, bob can not change the
773 // limit number
774 env(trust(gw, gw["USD"](50), alice, tfSetfAuth),
775 delegate::as(bob),
777 env.close();
778
779 // supported flags with correct permission
780 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::as(bob));
781 env.close();
782 env(delegate::set(
783 gw, bob, {"TrustlineAuthorize", "TrustlineFreeze"}));
784 env.close();
785 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
786 env.close();
787 env(delegate::set(
788 gw, bob, {"TrustlineAuthorize", "TrustlineUnfreeze"}));
789 env.close();
790 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
791 delegate::as(bob));
792 env.close();
793 // but bob can not freeze trustline because he no longer has freeze
794 // permission
795 env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
796 delegate::as(bob),
798
799 // cannot update LimitAmount with granular permission, both high and
800 // low account
801 env(trust(alice, gw["USD"](100)),
802 delegate::as(bob),
804 env(trust(gw, alice["USD"](100)),
805 delegate::as(bob),
807
808 // can not set QualityIn or QualityOut
809 auto tx = trust(alice, gw["USD"](50));
810 tx["QualityIn"] = "1000";
811 env(tx, delegate::as(bob), ter(tecNO_PERMISSION));
812 auto tx2 = trust(alice, gw["USD"](50));
813 tx2["QualityOut"] = "1000";
814 env(tx2, delegate::as(bob), ter(tecNO_PERMISSION));
815 auto tx3 = trust(gw, alice["USD"](50));
816 tx3["QualityIn"] = "1000";
817 env(tx3, delegate::as(bob), ter(tecNO_PERMISSION));
818 auto tx4 = trust(gw, alice["USD"](50));
819 tx4["QualityOut"] = "1000";
820 env(tx4, delegate::as(bob), ter(tecNO_PERMISSION));
821
822 // granting TrustSet can make it work
823 env(delegate::set(gw, bob, {"TrustSet"}));
824 env.close();
825 auto tx5 = trust(gw, alice["USD"](50));
826 tx5["QualityOut"] = "1000";
827 env(tx5, delegate::as(bob));
828 auto tx6 = trust(alice, gw["USD"](50));
829 tx6["QualityOut"] = "1000";
830 env(tx6, delegate::as(bob), ter(tecNO_PERMISSION));
831 env(delegate::set(alice, bob, {"TrustSet"}));
832 env.close();
833 env(tx6, delegate::as(bob));
834 }
835
836 // test mix of transaction level delegation and granular delegation
837 {
838 Env env(*this);
839 Account gw{"gw"};
840 Account alice{"alice"};
841 Account bob{"bob"};
842 env.fund(XRP(10000), gw, alice, bob);
843 env(fset(gw, asfRequireAuth));
844 env.close();
845
846 // bob does not have permission
847 env(trust(alice, gw["USD"](50)),
848 delegate::as(bob),
850 env(delegate::set(
851 alice, bob, {"TrustlineUnfreeze", "NFTokenCreateOffer"}));
852 env.close();
853 // bob still does not have permission
854 env(trust(alice, gw["USD"](50)),
855 delegate::as(bob),
857
858 // add TrustSet permission and some unrelated permission
859 env(delegate::set(
860 alice,
861 bob,
862 {"TrustlineUnfreeze",
863 "NFTokenCreateOffer",
864 "TrustSet",
865 "AccountTransferRateSet"}));
866 env.close();
867 env(trust(alice, gw["USD"](50)), delegate::as(bob));
868 env.close();
869
870 env(delegate::set(
871 gw,
872 bob,
873 {"TrustlineUnfreeze",
874 "NFTokenCreateOffer",
875 "TrustSet",
876 "AccountTransferRateSet"}));
877 env.close();
878
879 // since bob has TrustSet permission, he does not need
880 // TrustlineFreeze granular permission to freeze the trustline
881 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
882 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
883 delegate::as(bob));
884 // bob can perform all the operations regarding TrustSet
885 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
886 env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze),
887 delegate::as(bob));
888 env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze),
889 delegate::as(bob));
890 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::as(bob));
891 env(trust(alice, gw["USD"](50), tfSetNoRipple), delegate::as(bob));
892 env(trust(alice, gw["USD"](50), tfClearNoRipple),
893 delegate::as(bob));
894 }
895 }
896
897 void
899 {
900 testcase("test AccountSet granular permissions");
901 using namespace jtx;
902
903 // test AccountDomainSet, AccountEmailHashSet,
904 // AccountMessageKeySet,AccountTransferRateSet, and AccountTickSizeSet
905 // granular permissions
906 {
907 Env env(*this);
908 auto const alice = Account{"alice"};
909 auto const bob = Account{"bob"};
910 env.fund(XRP(10000), alice, bob);
911 env.close();
912
913 // alice gives bob some random permission, which is not related to
914 // the AccountSet transaction
915 env(delegate::set(alice, bob, {"TrustlineUnfreeze"}));
916 env.close();
917
918 // bob does not have permission to set domain
919 // on behalf of alice
920 std::string const domain = "example.com";
921 auto jt = noop(alice);
922 jt[sfDomain.fieldName] = strHex(domain);
923 jt[sfDelegate.fieldName] = bob.human();
924 jt[sfFlags.fieldName] = tfFullyCanonicalSig;
925
926 // add granular permission related to AccountSet but is not the
927 // correct permission for domain set
928 env(delegate::set(
929 alice, bob, {"TrustlineUnfreeze", "AccountEmailHashSet"}));
930 env.close();
931 env(jt, ter(tecNO_PERMISSION));
932
933 // alice give granular permission of AccountDomainSet to bob
934 env(delegate::set(alice, bob, {"AccountDomainSet"}));
935 env.close();
936
937 // bob set account domain on behalf of alice
938 env(jt);
939 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
940
941 // bob can reset domain
942 jt[sfDomain.fieldName] = "";
943 env(jt);
944 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfDomain));
945
946 // if flag is not equal to tfFullyCanonicalSig, which means bob
947 // is trying to set the flag at the same time, it will fail
948 std::string const failDomain = "fail_domain_update";
949 jt[sfFlags.fieldName] = tfRequireAuth;
950 jt[sfDomain.fieldName] = strHex(failDomain);
951 env(jt, ter(tecNO_PERMISSION));
952 // reset flag number
953 jt[sfFlags.fieldName] = tfFullyCanonicalSig;
954
955 // bob tries to update domain and set email hash,
956 // but he does not have permission to set email hash
957 jt[sfDomain.fieldName] = strHex(domain);
958 std::string const mh("5F31A79367DC3137FADA860C05742EE6");
959 jt[sfEmailHash.fieldName] = mh;
960 env(jt, ter(tecNO_PERMISSION));
961
962 // alice give granular permission of AccountEmailHashSet to bob
963 env(delegate::set(
964 alice, bob, {"AccountDomainSet", "AccountEmailHashSet"}));
965 env.close();
966 env(jt);
967 BEAST_EXPECT(to_string((*env.le(alice))[sfEmailHash]) == mh);
968 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
969
970 // bob does not have permission to set message key for alice
971 auto const rkp = randomKeyPair(KeyType::ed25519);
972 jt[sfMessageKey.fieldName] = strHex(rkp.first.slice());
973 env(jt, ter(tecNO_PERMISSION));
974
975 // alice give granular permission of AccountMessageKeySet to bob
976 env(delegate::set(
977 alice,
978 bob,
979 {"AccountDomainSet",
980 "AccountEmailHashSet",
981 "AccountMessageKeySet"}));
982 env.close();
983
984 // bob can set message key for alice
985 env(jt);
986 BEAST_EXPECT(
987 strHex((*env.le(alice))[sfMessageKey]) ==
988 strHex(rkp.first.slice()));
989 jt[sfMessageKey.fieldName] = "";
990 env(jt);
991 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfMessageKey));
992
993 // bob does not have permission to set transfer rate for alice
994 env(rate(alice, 2.0), delegate::as(bob), ter(tecNO_PERMISSION));
995
996 // alice give granular permission of AccountTransferRateSet to bob
997 env(delegate::set(
998 alice,
999 bob,
1000 {"AccountDomainSet",
1001 "AccountEmailHashSet",
1002 "AccountMessageKeySet",
1003 "AccountTransferRateSet"}));
1004 env.close();
1005 auto jtRate = rate(alice, 2.0);
1006 jtRate[sfDelegate.fieldName] = bob.human();
1007 jtRate[sfFlags.fieldName] = tfFullyCanonicalSig;
1008 env(jtRate, delegate::as(bob));
1009 BEAST_EXPECT((*env.le(alice))[sfTransferRate] == 2000000000);
1010
1011 // bob does not have permission to set ticksize for alice
1012 jt[sfTickSize.fieldName] = 8;
1013 env(jt, ter(tecNO_PERMISSION));
1014
1015 // alice give granular permission of AccountTickSizeSet to bob
1016 env(delegate::set(
1017 alice,
1018 bob,
1019 {"AccountDomainSet",
1020 "AccountEmailHashSet",
1021 "AccountMessageKeySet",
1022 "AccountTransferRateSet",
1023 "AccountTickSizeSet"}));
1024 env.close();
1025 env(jt);
1026 BEAST_EXPECT((*env.le(alice))[sfTickSize] == 8);
1027
1028 // can not set asfRequireAuth flag for alice
1029 env(fset(alice, asfRequireAuth),
1030 delegate::as(bob),
1032
1033 // reset Delegate will delete the Delegate
1034 // object
1035 env(delegate::set(alice, bob, {}));
1036 // bib still does not have permission to set asfRequireAuth for
1037 // alice
1038 env(fset(alice, asfRequireAuth),
1039 delegate::as(bob),
1041 // alice can set for herself
1042 env(fset(alice, asfRequireAuth));
1043 env.require(flags(alice, asfRequireAuth));
1044 env.close();
1045
1046 // can not update tick size because bob no longer has permission
1047 jt[sfTickSize.fieldName] = 7;
1048 env(jt, ter(tecNO_PERMISSION));
1049
1050 env(delegate::set(
1051 alice,
1052 bob,
1053 {"AccountDomainSet",
1054 "AccountEmailHashSet",
1055 "AccountMessageKeySet"}));
1056 env.close();
1057
1058 // bob does not have permission to set wallet locater for alice
1059 std::string const locator =
1060 "9633EC8AF54F16B5286DB1D7B519EF49EEFC050C0C8AC4384F1D88ACD1BFDF"
1061 "05";
1062 auto jt2 = noop(alice);
1063 jt2[sfDomain.fieldName] = strHex(domain);
1064 jt2[sfDelegate.fieldName] = bob.human();
1065 jt2[sfWalletLocator.fieldName] = locator;
1066 jt2[sfFlags.fieldName] = tfFullyCanonicalSig;
1067 env(jt2, ter(tecNO_PERMISSION));
1068 }
1069
1070 // can not set AccountSet flags on behalf of other account
1071 {
1072 Env env(*this);
1073 auto const alice = Account{"alice"};
1074 auto const bob = Account{"bob"};
1075 env.fund(XRP(10000), alice, bob);
1076 env.close();
1077
1078 auto testSetClearFlag = [&](std::uint32_t flag) {
1079 // bob can not set flag on behalf of alice
1080 env(fset(alice, flag),
1081 delegate::as(bob),
1083 // alice set by herself
1084 env(fset(alice, flag));
1085 env.close();
1086 env.require(flags(alice, flag));
1087 // bob can not clear on behalf of alice
1088 env(fclear(alice, flag),
1089 delegate::as(bob),
1091 };
1092
1093 // testSetClearFlag(asfNoFreeze);
1094 testSetClearFlag(asfRequireAuth);
1095 testSetClearFlag(asfAllowTrustLineClawback);
1096
1097 // alice gives some granular permissions to bob
1098 env(delegate::set(
1099 alice,
1100 bob,
1101 {"AccountDomainSet",
1102 "AccountEmailHashSet",
1103 "AccountMessageKeySet"}));
1104 env.close();
1105
1106 testSetClearFlag(asfDefaultRipple);
1107 testSetClearFlag(asfDepositAuth);
1108 testSetClearFlag(asfDisallowIncomingCheck);
1109 testSetClearFlag(asfDisallowIncomingNFTokenOffer);
1110 testSetClearFlag(asfDisallowIncomingPayChan);
1111 testSetClearFlag(asfDisallowIncomingTrustline);
1112 testSetClearFlag(asfDisallowXRP);
1113 testSetClearFlag(asfRequireDest);
1114 testSetClearFlag(asfGlobalFreeze);
1115
1116 // bob can not set asfAccountTxnID on behalf of alice
1117 env(fset(alice, asfAccountTxnID),
1118 delegate::as(bob),
1120 env(fset(alice, asfAccountTxnID));
1121 env.close();
1122 BEAST_EXPECT(env.le(alice)->isFieldPresent(sfAccountTxnID));
1123 env(fclear(alice, asfAccountTxnID),
1124 delegate::as(bob),
1126
1127 // bob can not set asfAuthorizedNFTokenMinter on behalf of alice
1129 jt[sfDelegate.fieldName] = bob.human();
1130 jt[sfNFTokenMinter.fieldName] = bob.human();
1131 env(jt, ter(tecNO_PERMISSION));
1132
1133 // bob gives alice some permissions
1134 env(delegate::set(
1135 bob,
1136 alice,
1137 {"AccountDomainSet",
1138 "AccountEmailHashSet",
1139 "AccountMessageKeySet"}));
1140 env.close();
1141
1142 // since we can not set asfNoFreeze if asfAllowTrustLineClawback is
1143 // set, which can not be clear either. Test alice set asfNoFreeze on
1144 // behalf of bob.
1145 env(fset(alice, asfNoFreeze),
1146 delegate::as(bob),
1148 env(fset(bob, asfNoFreeze));
1149 env.close();
1150 env.require(flags(bob, asfNoFreeze));
1151 // alice can not clear on behalf of bob
1152 env(fclear(alice, asfNoFreeze),
1153 delegate::as(bob),
1155
1156 // bob can not set asfDisableMaster on behalf of alice
1157 Account const bobKey{"bobKey", KeyType::secp256k1};
1158 env(regkey(bob, bobKey));
1159 env.close();
1160 env(fset(alice, asfDisableMaster),
1161 delegate::as(bob),
1162 sig(bob),
1164 }
1165 }
1166
1167 void
1169 {
1170 testcase("test MPTokenIssuanceSet granular");
1171 using namespace jtx;
1172
1173 // test MPTokenIssuanceUnlock and MPTokenIssuanceLock permissions
1174 {
1175 Env env(*this);
1176 Account alice{"alice"};
1177 Account bob{"bob"};
1178 env.fund(XRP(100000), alice, bob);
1179 env.close();
1180
1181 MPTTester mpt(env, alice, {.fund = false});
1182 env.close();
1183 mpt.create({.flags = tfMPTCanLock});
1184 env.close();
1185
1186 // delegate ledger object is not created yet
1187 mpt.set(
1188 {.account = alice,
1189 .flags = tfMPTLock,
1190 .delegate = bob,
1191 .err = tecNO_PERMISSION});
1192
1193 // alice gives granular permission to bob of MPTokenIssuanceUnlock
1194 env(delegate::set(alice, bob, {"MPTokenIssuanceUnlock"}));
1195 env.close();
1196 // bob does not have lock permission
1197 mpt.set(
1198 {.account = alice,
1199 .flags = tfMPTLock,
1200 .delegate = bob,
1201 .err = tecNO_PERMISSION});
1202 // bob now has lock permission, but does not have unlock permission
1203 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
1204 env.close();
1205 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1206 mpt.set(
1207 {.account = alice,
1208 .flags = tfMPTUnlock,
1209 .delegate = bob,
1210 .err = tecNO_PERMISSION});
1211
1212 // now bob can lock and unlock
1213 env(delegate::set(
1214 alice, bob, {"MPTokenIssuanceLock", "MPTokenIssuanceUnlock"}));
1215 env.close();
1216 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1217 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1218 env.close();
1219 }
1220
1221 // test mix of granular and transaction level permission
1222 {
1223 Env env(*this);
1224 Account alice{"alice"};
1225 Account bob{"bob"};
1226 env.fund(XRP(100000), alice, bob);
1227 env.close();
1228
1229 MPTTester mpt(env, alice, {.fund = false});
1230 env.close();
1231 mpt.create({.flags = tfMPTCanLock});
1232 env.close();
1233
1234 // alice gives granular permission to bob of MPTokenIssuanceLock
1235 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
1236 env.close();
1237 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1238 // bob does not have unlock permission
1239 mpt.set(
1240 {.account = alice,
1241 .flags = tfMPTUnlock,
1242 .delegate = bob,
1243 .err = tecNO_PERMISSION});
1244
1245 // alice gives bob some unrelated permission with
1246 // MPTokenIssuanceLock
1247 env(delegate::set(
1248 alice,
1249 bob,
1250 {"NFTokenMint", "MPTokenIssuanceLock", "NFTokenBurn"}));
1251 env.close();
1252 // bob can not unlock
1253 mpt.set(
1254 {.account = alice,
1255 .flags = tfMPTUnlock,
1256 .delegate = bob,
1257 .err = tecNO_PERMISSION});
1258
1259 // alice add MPTokenIssuanceSet to permissions
1260 env(delegate::set(
1261 alice,
1262 bob,
1263 {"NFTokenMint",
1264 "MPTokenIssuanceLock",
1265 "NFTokenBurn",
1266 "MPTokenIssuanceSet"}));
1267 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1268 // alice can lock by herself
1269 mpt.set({.account = alice, .flags = tfMPTLock});
1270 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1271 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1272 }
1273 }
1274
1275 void
1277 {
1278 testcase("test single sign");
1279 using namespace jtx;
1280
1281 Env env(*this);
1282 Account alice{"alice"};
1283 Account bob{"bob"};
1284 Account carol{"carol"};
1285 env.fund(XRP(100000), alice, bob, carol);
1286 env.close();
1287
1288 env(delegate::set(alice, bob, {"Payment"}));
1289 env.close();
1290
1291 auto aliceBalance = env.balance(alice);
1292 auto bobBalance = env.balance(bob);
1293 auto carolBalance = env.balance(carol);
1294
1295 env(pay(alice, carol, XRP(100)),
1296 fee(XRP(10)),
1297 delegate::as(bob),
1298 sig(bob));
1299 env.close();
1300 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100));
1301 BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10));
1302 BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(100));
1303 }
1304
1305 void
1307 {
1308 testcase("test single sign with bad secret");
1309 using namespace jtx;
1310
1311 Env env(*this);
1312 Account alice{"alice"};
1313 Account bob{"bob"};
1314 Account carol{"carol"};
1315 env.fund(XRP(100000), alice, bob, carol);
1316 env.close();
1317
1318 env(delegate::set(alice, bob, {"Payment"}));
1319 env.close();
1320
1321 auto aliceBalance = env.balance(alice);
1322 auto bobBalance = env.balance(bob);
1323 auto carolBalance = env.balance(carol);
1324
1325 env(pay(alice, carol, XRP(100)),
1326 fee(XRP(10)),
1327 delegate::as(bob),
1328 sig(alice),
1329 ter(tefBAD_AUTH));
1330 env.close();
1331 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1332 BEAST_EXPECT(env.balance(bob) == bobBalance);
1333 BEAST_EXPECT(env.balance(carol) == carolBalance);
1334 }
1335
1336 void
1338 {
1339 testcase("test multi sign");
1340 using namespace jtx;
1341
1342 Env env(*this);
1343 Account alice{"alice"};
1344 Account bob{"bob"};
1345 Account carol{"carol"};
1346 Account daria{"daria"};
1347 Account edward{"edward"};
1348 env.fund(XRP(100000), alice, bob, carol, daria, edward);
1349 env.close();
1350
1351 env(signers(bob, 2, {{daria, 1}, {edward, 1}}));
1352 env.close();
1353
1354 env(delegate::set(alice, bob, {"Payment"}));
1355 env.close();
1356
1357 auto aliceBalance = env.balance(alice);
1358 auto bobBalance = env.balance(bob);
1359 auto carolBalance = env.balance(carol);
1360 auto dariaBalance = env.balance(daria);
1361 auto edwardBalance = env.balance(edward);
1362
1363 env(pay(alice, carol, XRP(100)),
1364 fee(XRP(10)),
1365 delegate::as(bob),
1366 msig(daria, edward));
1367 env.close();
1368 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100));
1369 BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10));
1370 BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(100));
1371 BEAST_EXPECT(env.balance(daria) == dariaBalance);
1372 BEAST_EXPECT(env.balance(edward) == edwardBalance);
1373 }
1374
1375 void
1377 {
1378 testcase("test multi sign which does not meet quorum");
1379 using namespace jtx;
1380
1381 Env env(*this);
1382 Account alice{"alice"};
1383 Account bob{"bob"};
1384 Account carol{"carol"};
1385 Account daria = Account{"daria"};
1386 Account edward = Account{"edward"};
1387 Account fred = Account{"fred"};
1388 env.fund(XRP(100000), alice, bob, carol, daria, edward, fred);
1389 env.close();
1390
1391 env(signers(bob, 3, {{daria, 1}, {edward, 1}, {fred, 1}}));
1392 env.close();
1393
1394 env(delegate::set(alice, bob, {"Payment"}));
1395 env.close();
1396
1397 auto aliceBalance = env.balance(alice);
1398 auto bobBalance = env.balance(bob);
1399 auto carolBalance = env.balance(carol);
1400 auto dariaBalance = env.balance(daria);
1401 auto edwardBalance = env.balance(edward);
1402
1403 env(pay(alice, carol, XRP(100)),
1404 fee(XRP(10)),
1405 delegate::as(bob),
1406 msig(daria, edward),
1408 env.close();
1409 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1410 BEAST_EXPECT(env.balance(bob) == bobBalance);
1411 BEAST_EXPECT(env.balance(carol) == carolBalance);
1412 BEAST_EXPECT(env.balance(daria) == dariaBalance);
1413 BEAST_EXPECT(env.balance(edward) == edwardBalance);
1414 }
1415
1416 void
1417 run() override
1418 {
1422 testReserve();
1423 testFee();
1424 testSequence();
1433 testMultiSign();
1435 }
1436};
1437BEAST_DEFINE_TESTSUITE(Delegate, app, ripple);
1438} // namespace test
1439} // 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:66
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:441
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:30
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:82
constexpr std::uint32_t asfDepositAuth
Definition: TxFlags.h:84
constexpr std::uint32_t asfDisallowIncomingNFTokenOffer
Definition: TxFlags.h:89
constexpr std::uint32_t tfSetDeepFreeze
Definition: TxFlags.h:117
constexpr std::uint32_t asfRequireDest
Definition: TxFlags.h:76
constexpr std::uint32_t const tfMPTUnlock
Definition: TxFlags.h:163
constexpr std::uint32_t asfAuthorizedNFTokenMinter
Definition: TxFlags.h:85
constexpr std::uint32_t asfNoFreeze
Definition: TxFlags.h:81
constexpr std::uint32_t asfDisableMaster
Definition: TxFlags.h:79
constexpr std::uint32_t asfDisallowIncomingTrustline
Definition: TxFlags.h:92
@ tefBAD_AUTH
Definition: TER.h:169
@ tefBAD_QUORUM
Definition: TER.h:180
constexpr std::uint32_t tfClearNoRipple
Definition: TxFlags.h:114
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
constexpr std::uint32_t tfSetfAuth
Definition: TxFlags.h:112
constexpr std::uint32_t asfAccountTxnID
Definition: TxFlags.h:80
constexpr std::uint32_t asfDefaultRipple
Definition: TxFlags.h:83
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:90
constexpr std::uint32_t tfClearFreeze
Definition: TxFlags.h:116
constexpr std::uint32_t tfRequireAuth
Definition: TxFlags.h:67
@ 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:162
constexpr std::uint32_t tfClearDeepFreeze
Definition: TxFlags.h:118
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
constexpr std::uint32_t asfDisallowIncomingPayChan
Definition: TxFlags.h:91
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:93
constexpr std::uint32_t asfRequireAuth
Definition: TxFlags.h:77
@ terINSUF_FEE_B
Definition: TER.h:216
@ terNO_ACCOUNT
Definition: TER.h:217
constexpr std::uint32_t tfSetFreeze
Definition: TxFlags.h:115
constexpr std::uint32_t tfSetNoRipple
Definition: TxFlags.h:113
constexpr std::uint32_t const tfMPTCanLock
Definition: TxFlags.h:148
constexpr std::uint32_t asfDisallowXRP
Definition: TxFlags.h:78
@ 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