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