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