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