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/CaptureLogs.h>
20#include <test/jtx/delegate.h>
21
22#include <xrpl/protocol/Feature.h>
23#include <xrpl/protocol/Permissions.h>
24
25namespace ripple {
26namespace test {
28{
29 void
31 {
32 testcase("test feature not enabled");
33 using namespace jtx;
34
35 Env env{*this, features};
36 Account gw{"gateway"};
37 Account alice{"alice"};
38 Account bob{"bob"};
39 env.fund(XRP(1000000), gw, alice, bob);
40 env.close();
41
42 auto res = features[featurePermissionDelegationV1_1] ? ter(tesSUCCESS)
44
45 // can not set Delegate when feature disabled
46 env(delegate::set(gw, alice, {"Payment"}), res);
47 env.close();
48
49 // can not send delegating transaction when feature disabled
50 env(pay(gw, bob, XRP(100)), delegate::as(alice), res);
51 }
52
53 void
55 {
56 testcase("test valid request creating, updating, deleting permissions");
57 using namespace jtx;
58
59 Env env(*this);
60 Account gw{"gateway"};
61 Account alice{"alice"};
62 env.fund(XRP(100000), gw, alice);
63 env.close();
64
65 // delegating an empty permission list when the delegate ledger object
66 // does not exist will not create the ledger object
67 env(delegate::set(gw, alice, std::vector<std::string>{}));
68 env.close();
69 auto const entry = delegate::entry(env, gw, alice);
70 BEAST_EXPECT(entry[jss::result][jss::error] == "entryNotFound");
71
72 auto const permissions = std::vector<std::string>{
73 "Payment",
74 "EscrowCreate",
75 "EscrowFinish",
76 "TrustlineAuthorize",
77 "CheckCreate"};
78 env(delegate::set(gw, alice, permissions));
79 env.close();
80
81 // this lambda function is used to compare the json value of ledger
82 // entry response with the given vector of permissions.
83 auto comparePermissions =
84 [&](Json::Value const& jle,
85 std::vector<std::string> const& permissions,
86 Account const& account,
87 Account const& authorize) {
88 BEAST_EXPECT(
89 !jle[jss::result].isMember(jss::error) &&
90 jle[jss::result].isMember(jss::node));
91 BEAST_EXPECT(
92 jle[jss::result][jss::node]["LedgerEntryType"] ==
93 jss::Delegate);
94 BEAST_EXPECT(
95 jle[jss::result][jss::node][jss::Account] ==
96 account.human());
97 BEAST_EXPECT(
98 jle[jss::result][jss::node][sfAuthorize.jsonName] ==
99 authorize.human());
100
101 auto const& jPermissions =
102 jle[jss::result][jss::node][sfPermissions.jsonName];
103 unsigned i = 0;
104 for (auto const& permission : permissions)
105 {
106 BEAST_EXPECT(
107 jPermissions[i][sfPermission.jsonName]
108 [sfPermissionValue.jsonName] == permission);
109 i++;
110 }
111 };
112
113 // get ledger entry with valid parameter
114 comparePermissions(
115 delegate::entry(env, gw, alice), permissions, gw, alice);
116
117 // gw updates permission
118 auto const newPermissions = std::vector<std::string>{
119 "Payment", "AMMCreate", "AMMDeposit", "AMMWithdraw"};
120 env(delegate::set(gw, alice, newPermissions));
121 env.close();
122
123 // get ledger entry again, permissions should be updated to
124 // newPermissions
125 comparePermissions(
126 delegate::entry(env, gw, alice), newPermissions, gw, alice);
127
128 // gw deletes all permissions delegated to alice, this will delete
129 // the
130 // ledger entry
131 env(delegate::set(gw, alice, {}));
132 env.close();
133 auto const jle = delegate::entry(env, gw, alice);
134 BEAST_EXPECT(jle[jss::result][jss::error] == "entryNotFound");
135
136 // alice can delegate permissions to gw as well
137 env(delegate::set(alice, gw, permissions));
138 env.close();
139 comparePermissions(
140 delegate::entry(env, alice, gw), permissions, alice, gw);
141 auto const response = delegate::entry(env, gw, alice);
142 // alice has not been granted any permissions by gw
143 BEAST_EXPECT(response[jss::result][jss::error] == "entryNotFound");
144 }
145
146 void
148 {
149 testcase("test invalid DelegateSet");
150 using namespace jtx;
151
152 Env env(*this, features);
153 Account gw{"gateway"};
154 Account alice{"alice"};
155 Account bob{"bob"};
156 env.fund(XRP(100000), gw, alice, bob);
157 env.close();
158
159 // when permissions size exceeds the limit 10, should return
160 // temARRAY_TOO_LARGE
161 {
162 env(delegate::set(
163 gw,
164 alice,
165 {"Payment",
166 "EscrowCreate",
167 "EscrowFinish",
168 "EscrowCancel",
169 "CheckCreate",
170 "CheckCash",
171 "CheckCancel",
172 "DepositPreauth",
173 "TrustSet",
174 "NFTokenMint",
175 "NFTokenBurn"}),
177 }
178
179 // alice can not authorize herself
180 {
181 env(delegate::set(alice, alice, {"Payment"}), ter(temMALFORMED));
182 }
183
184 // bad fee
185 {
186 Json::Value jv;
187 jv[jss::TransactionType] = jss::DelegateSet;
188 jv[jss::Account] = gw.human();
189 jv[sfAuthorize.jsonName] = alice.human();
190 Json::Value permissionsJson(Json::arrayValue);
191 Json::Value permissionValue;
192 permissionValue[sfPermissionValue.jsonName] = "Payment";
193 Json::Value permissionObj;
194 permissionObj[sfPermission.jsonName] = permissionValue;
195 permissionsJson.append(permissionObj);
196 jv[sfPermissions.jsonName] = permissionsJson;
197 jv[sfFee.jsonName] = -1;
198 env(jv, ter(temBAD_FEE));
199 }
200
201 // when provided permissions contains duplicate values, should return
202 // temMALFORMED
203 {
204 env(delegate::set(
205 gw,
206 alice,
207 {"Payment",
208 "EscrowCreate",
209 "EscrowFinish",
210 "TrustlineAuthorize",
211 "CheckCreate",
212 "TrustlineAuthorize"}),
214 }
215
216 // when authorizing account which does not exist, should return
217 // tecNO_TARGET
218 {
219 env(delegate::set(gw, Account("unknown"), {"Payment"}),
221 }
222
223 // non-delegatable transaction
224 {
225 env(delegate::set(gw, alice, {"SetRegularKey"}), ter(temMALFORMED));
226 env(delegate::set(gw, alice, {"AccountSet"}), ter(temMALFORMED));
227 env(delegate::set(gw, alice, {"SignerListSet"}), ter(temMALFORMED));
228 env(delegate::set(gw, alice, {"DelegateSet"}), ter(temMALFORMED));
229 env(delegate::set(gw, alice, {"EnableAmendment"}),
231 env(delegate::set(gw, alice, {"UNLModify"}), ter(temMALFORMED));
232 env(delegate::set(gw, alice, {"SetFee"}), ter(temMALFORMED));
233 env(delegate::set(gw, alice, {"Batch"}), ter(temMALFORMED));
234 }
235 }
236
237 void
239 {
240 testcase("test reserve");
241 using namespace jtx;
242
243 // test reserve for DelegateSet
244 {
245 Env env(*this);
246 Account alice{"alice"};
247 Account bob{"bob"};
248 Account carol{"carol"};
249
250 env.fund(drops(env.current()->fees().accountReserve(0)), alice);
251 env.fund(
252 drops(env.current()->fees().accountReserve(1)), bob, carol);
253 env.close();
254
255 // alice does not have enough reserve to create Delegate
256 env(delegate::set(alice, bob, {"Payment"}),
258
259 // bob has enough reserve
260 env(delegate::set(bob, alice, {"Payment"}));
261 env.close();
262
263 // now bob create another Delegate, he does not have
264 // enough reserve
265 env(delegate::set(bob, carol, {"Payment"}),
267 }
268
269 // test reserve when sending transaction on behalf of other account
270 {
271 Env env(*this);
272 Account alice{"alice"};
273 Account bob{"bob"};
274
275 env.fund(drops(env.current()->fees().accountReserve(1)), alice);
276 env.fund(drops(env.current()->fees().accountReserve(2)), bob);
277 env.close();
278
279 // alice gives bob permission
280 env(delegate::set(alice, bob, {"DIDSet", "DIDDelete"}));
281 env.close();
282
283 // bob set DID on behalf of alice, but alice does not have enough
284 // reserve
285 env(did::set(alice),
286 did::uri("uri"),
287 delegate::as(bob),
289
290 // bob can set DID for himself because he has enough reserve
291 env(did::set(bob), did::uri("uri"));
292 env.close();
293 }
294 }
295
296 void
298 {
299 testcase("test fee");
300 using namespace jtx;
301
302 Env env(*this);
303 Account alice{"alice"};
304 Account bob{"bob"};
305 Account carol{"carol"};
306 env.fund(XRP(10000), alice, carol);
307 env.fund(XRP(1000), bob);
308 env.close();
309
310 {
311 auto aliceBalance = env.balance(alice);
312 auto bobBalance = env.balance(bob);
313 auto carolBalance = env.balance(carol);
314
315 env(pay(alice, carol, XRP(100)),
316 fee(XRP(2000)),
317 delegate::as(bob),
319 env.close();
320 BEAST_EXPECT(env.balance(alice) == aliceBalance);
321 BEAST_EXPECT(env.balance(bob) == bobBalance);
322 BEAST_EXPECT(env.balance(carol) == carolBalance);
323 }
324
325 env(delegate::set(alice, bob, {"Payment"}));
326 env.close();
327
328 {
329 // Delegate pays the fee
330 auto aliceBalance = env.balance(alice);
331 auto bobBalance = env.balance(bob);
332 auto carolBalance = env.balance(carol);
333
334 auto const sendAmt = XRP(100);
335 auto const feeAmt = XRP(10);
336 env(pay(alice, carol, sendAmt), fee(feeAmt), delegate::as(bob));
337 env.close();
338 BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt);
339 BEAST_EXPECT(env.balance(bob) == bobBalance - feeAmt);
340 BEAST_EXPECT(env.balance(carol) == carolBalance + sendAmt);
341 }
342
343 {
344 // insufficient balance to pay fee
345 auto aliceBalance = env.balance(alice);
346 auto bobBalance = env.balance(bob);
347 auto carolBalance = env.balance(carol);
348
349 env(pay(alice, carol, XRP(100)),
350 fee(XRP(2000)),
351 delegate::as(bob),
353 env.close();
354 BEAST_EXPECT(env.balance(alice) == aliceBalance);
355 BEAST_EXPECT(env.balance(bob) == bobBalance);
356 BEAST_EXPECT(env.balance(carol) == carolBalance);
357 }
358
359 {
360 // fee is paid by Delegate
361 // on context reset (tec error)
362 auto aliceBalance = env.balance(alice);
363 auto bobBalance = env.balance(bob);
364 auto carolBalance = env.balance(carol);
365 auto const feeAmt = XRP(10);
366
367 env(pay(alice, carol, XRP(20000)),
368 fee(feeAmt),
369 delegate::as(bob),
371 env.close();
372 BEAST_EXPECT(env.balance(alice) == aliceBalance);
373 BEAST_EXPECT(env.balance(bob) == bobBalance - feeAmt);
374 BEAST_EXPECT(env.balance(carol) == carolBalance);
375 }
376 }
377
378 void
380 {
381 testcase("test sequence");
382 using namespace jtx;
383
384 Env env(*this);
385 Account alice{"alice"};
386 Account bob{"bob"};
387 Account carol{"carol"};
388 env.fund(XRP(10000), alice, bob, carol);
389 env.close();
390
391 auto aliceSeq = env.seq(alice);
392 auto bobSeq = env.seq(bob);
393 env(delegate::set(alice, bob, {"Payment"}));
394 env(delegate::set(bob, alice, {"Payment"}));
395 env.close();
396 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
397 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
398 aliceSeq = env.seq(alice);
399 bobSeq = env.seq(bob);
400
401 for (auto i = 0; i < 20; ++i)
402 {
403 // bob is the delegated account, his sequence won't increment
404 env(pay(alice, carol, XRP(10)), fee(XRP(10)), delegate::as(bob));
405 env.close();
406 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
407 BEAST_EXPECT(env.seq(bob) == bobSeq);
408 aliceSeq = env.seq(alice);
409
410 // bob sends payment for himself, his sequence will increment
411 env(pay(bob, carol, XRP(10)), fee(XRP(10)));
412 BEAST_EXPECT(env.seq(alice) == aliceSeq);
413 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
414 bobSeq = env.seq(bob);
415
416 // alice is the delegated account, her sequence won't increment
417 env(pay(bob, carol, XRP(10)), fee(XRP(10)), delegate::as(alice));
418 env.close();
419 BEAST_EXPECT(env.seq(alice) == aliceSeq);
420 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
421 bobSeq = env.seq(bob);
422
423 // alice sends payment for herself, her sequence will increment
424 env(pay(alice, carol, XRP(10)), fee(XRP(10)));
425 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
426 BEAST_EXPECT(env.seq(bob) == bobSeq);
427 aliceSeq = env.seq(alice);
428 }
429 }
430
431 void
433 {
434 testcase("test deleting account");
435 using namespace jtx;
436
437 Env env(*this);
438 Account alice{"alice"};
439 Account bob{"bob"};
440 env.fund(XRP(100000), alice, bob);
441 env.close();
442
443 env(delegate::set(alice, bob, {"Payment"}));
444 env.close();
445 BEAST_EXPECT(
446 env.closed()->exists(keylet::delegate(alice.id(), bob.id())));
447
448 for (std::uint32_t i = 0; i < 256; ++i)
449 env.close();
450
451 auto const aliceBalance = env.balance(alice);
452 auto const bobBalance = env.balance(bob);
453
454 // alice deletes account, this will remove the Delegate object
455 auto const deleteFee = drops(env.current()->fees().increment);
456 env(acctdelete(alice, bob), fee(deleteFee));
457 env.close();
458
459 BEAST_EXPECT(!env.closed()->exists(keylet::account(alice.id())));
460 BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(alice.id())));
461 BEAST_EXPECT(env.balance(bob) == bobBalance + aliceBalance - deleteFee);
462
463 BEAST_EXPECT(
464 !env.closed()->exists(keylet::delegate(alice.id(), bob.id())));
465 }
466
467 void
469 {
470 testcase("test delegate transaction");
471 using namespace jtx;
472
473 Env env(*this);
474 Account alice{"alice"};
475 Account bob{"bob"};
476 Account carol{"carol"};
477
478 XRPAmount const baseFee{env.current()->fees().base};
479
480 // use different initial amount to distinguish the source balance
481 env.fund(XRP(10000), alice);
482 env.fund(XRP(20000), bob);
483 env.fund(XRP(30000), carol);
484 env.close();
485
486 auto aliceBalance = env.balance(alice, XRP);
487 auto bobBalance = env.balance(bob, XRP);
488 auto carolBalance = env.balance(carol, XRP);
489
490 // can not send transaction on one's own behalf
491 env(pay(alice, bob, XRP(50)), delegate::as(alice), ter(temBAD_SIGNER));
492 env.require(balance(alice, aliceBalance));
493
494 env(delegate::set(alice, bob, {"Payment"}));
495 env.close();
496 env.require(balance(alice, aliceBalance - drops(baseFee)));
497 aliceBalance = env.balance(alice, XRP);
498
499 // bob pays 50 XRP to carol on behalf of alice
500 env(pay(alice, carol, XRP(50)), delegate::as(bob));
501 env.close();
502 env.require(balance(alice, aliceBalance - XRP(50)));
503 env.require(balance(carol, carolBalance + XRP(50)));
504 // bob pays the fee
505 env.require(balance(bob, bobBalance - drops(baseFee)));
506 aliceBalance = env.balance(alice, XRP);
507 bobBalance = env.balance(bob, XRP);
508 carolBalance = env.balance(carol, XRP);
509
510 // bob pays 50 XRP to bob self on behalf of alice
511 env(pay(alice, bob, XRP(50)), delegate::as(bob));
512 env.close();
513 env.require(balance(alice, aliceBalance - XRP(50)));
514 env.require(balance(bob, bobBalance + XRP(50) - drops(baseFee)));
515 aliceBalance = env.balance(alice, XRP);
516 bobBalance = env.balance(bob, XRP);
517
518 // bob pay 50 XRP to alice herself on behalf of alice
519 env(pay(alice, alice, XRP(50)), delegate::as(bob), ter(temREDUNDANT));
520 env.close();
521
522 // bob does not have permission to create check
523 env(check::create(alice, bob, XRP(10)),
524 delegate::as(bob),
526
527 // carol does not have permission to create check
528 env(check::create(alice, bob, XRP(10)),
529 delegate::as(carol),
531 }
532
533 void
535 {
536 testcase("test payment granular");
537 using namespace jtx;
538
539 // test PaymentMint and PaymentBurn
540 {
541 Env env(*this);
542 Account alice{"alice"};
543 Account bob{"bob"};
544 Account gw{"gateway"};
545 Account gw2{"gateway2"};
546 auto const USD = gw["USD"];
547 auto const EUR = gw2["EUR"];
548
549 env.fund(XRP(10000), alice);
550 env.fund(XRP(20000), bob);
551 env.fund(XRP(40000), gw, gw2);
552 env.trust(USD(200), alice);
553 env.trust(EUR(400), gw);
554 env.close();
555
556 XRPAmount const baseFee{env.current()->fees().base};
557 auto aliceBalance = env.balance(alice, XRP);
558 auto bobBalance = env.balance(bob, XRP);
559 auto gwBalance = env.balance(gw, XRP);
560 auto gw2Balance = env.balance(gw2, XRP);
561
562 // delegate ledger object is not created yet
563 env(pay(gw, alice, USD(50)),
564 delegate::as(bob),
566 env.require(balance(bob, bobBalance));
567
568 // gw gives bob burn permission
569 env(delegate::set(gw, bob, {"PaymentBurn"}));
570 env.close();
571 env.require(balance(gw, gwBalance - drops(baseFee)));
572 gwBalance = env.balance(gw, XRP);
573
574 // bob sends a payment transaction on behalf of gw
575 env(pay(gw, alice, USD(50)),
576 delegate::as(bob),
578 env.close();
579 env.require(balance(bob, bobBalance));
580
581 // gw gives bob mint permission, alice gives bob burn permission
582 env(delegate::set(gw, bob, {"PaymentMint"}));
583 env(delegate::set(alice, bob, {"PaymentBurn"}));
584 env.close();
585 env.require(balance(alice, aliceBalance - drops(baseFee)));
586 env.require(balance(gw, gwBalance - drops(baseFee)));
587 aliceBalance = env.balance(alice, XRP);
588 gwBalance = env.balance(gw, XRP);
589
590 // can not send XRP
591 env(pay(gw, alice, XRP(50)),
592 delegate::as(bob),
594 env.close();
595 env.require(balance(bob, bobBalance));
596
597 // mint 50 USD
598 env(pay(gw, alice, USD(50)), delegate::as(bob));
599 env.close();
600 env.require(balance(bob, bobBalance - drops(baseFee)));
601 env.require(balance(gw, gwBalance));
602 env.require(balance(gw, alice["USD"](-50)));
603 env.require(balance(alice, USD(50)));
604 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
605 bobBalance = env.balance(bob, XRP);
606
607 // burn 30 USD
608 env(pay(alice, gw, USD(30)), delegate::as(bob));
609 env.close();
610 env.require(balance(bob, bobBalance - drops(baseFee)));
611 env.require(balance(gw, gwBalance));
612 env.require(balance(gw, alice["USD"](-20)));
613 env.require(balance(alice, USD(20)));
614 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
615 bobBalance = env.balance(bob, XRP);
616
617 // bob has both mint and burn permissions
618 env(delegate::set(gw, bob, {"PaymentMint", "PaymentBurn"}));
619 env.close();
620 env.require(balance(gw, gwBalance - drops(baseFee)));
621 gwBalance = env.balance(gw, XRP);
622
623 // mint 100 USD for gw
624 env(pay(gw, alice, USD(100)), delegate::as(bob));
625 env.close();
626 env.require(balance(gw, alice["USD"](-120)));
627 env.require(balance(alice, USD(120)));
628 env.require(balance(bob, bobBalance - drops(baseFee)));
629 bobBalance = env.balance(bob, XRP);
630
631 // gw2 pays gw 200 EUR
632 env(pay(gw2, gw, EUR(200)));
633 env.close();
634 env.require(balance(gw2, gw2Balance - drops(baseFee)));
635 gw2Balance = env.balance(gw2, XRP);
636 env.require(balance(gw2, gw["EUR"](-200)));
637 env.require(balance(gw, EUR(200)));
638
639 // burn 100 EUR for gw
640 env(pay(gw, gw2, EUR(100)), delegate::as(bob));
641 env.close();
642 env.require(balance(gw2, gw["EUR"](-100)));
643 env.require(balance(gw, EUR(100)));
644 env.require(balance(bob, bobBalance - drops(baseFee)));
645 env.require(balance(gw, gwBalance));
646 env.require(balance(gw2, gw2Balance));
647 env.require(balance(alice, aliceBalance));
648 }
649
650 // test PaymentMint won't affect Payment transaction level delegation.
651 {
652 Env env(*this);
653 Account alice{"alice"};
654 Account bob{"bob"};
655 Account gw{"gateway"};
656 auto const USD = gw["USD"];
657
658 env.fund(XRP(10000), alice);
659 env.fund(XRP(20000), bob);
660 env.fund(XRP(40000), gw);
661 env.trust(USD(200), alice);
662 env.close();
663
664 XRPAmount const baseFee{env.current()->fees().base};
665
666 auto aliceBalance = env.balance(alice, XRP);
667 auto bobBalance = env.balance(bob, XRP);
668 auto gwBalance = env.balance(gw, XRP);
669
670 // gw gives bob PaymentBurn permission
671 env(delegate::set(gw, bob, {"PaymentBurn"}));
672 env.close();
673 env.require(balance(gw, gwBalance - drops(baseFee)));
674 gwBalance = env.balance(gw, XRP);
675
676 // bob can not mint on behalf of gw because he only has burn
677 // permission
678 env(pay(gw, alice, USD(50)),
679 delegate::as(bob),
681 env.close();
682 env.require(balance(bob, bobBalance));
683
684 // gw gives bob Payment permission as well
685 env(delegate::set(gw, bob, {"PaymentBurn", "Payment"}));
686 env.close();
687 env.require(balance(gw, gwBalance - drops(baseFee)));
688 gwBalance = env.balance(gw, XRP);
689
690 // bob now can mint on behalf of gw
691 env(pay(gw, alice, USD(50)), delegate::as(bob));
692 env.close();
693 env.require(balance(bob, bobBalance - drops(baseFee)));
694 env.require(balance(gw, gwBalance));
695 env.require(balance(alice, aliceBalance));
696 env.require(balance(gw, alice["USD"](-50)));
697 env.require(balance(alice, USD(50)));
698 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
699 }
700
701 // disallow cross currency payment with only PaymentBurn/PaymentMint
702 // permission
703 {
704 Env env(*this, features);
705 Account const alice{"alice"};
706 Account const bob{"bob"};
707 Account const gw{"gateway"};
708 Account const carol{"carol"};
709 auto const USD = gw["USD"];
710
711 env.fund(XRP(10000), alice, bob, carol, gw);
712 env.close();
713 env.trust(USD(50000), alice);
714 env.trust(USD(50000), bob);
715 env.trust(USD(50000), carol);
716 env(pay(gw, alice, USD(10000)));
717 env(pay(gw, bob, USD(10000)));
718 env(pay(gw, carol, USD(10000)));
719 env.close();
720
721 // PaymentMint
722 {
723 env(offer(carol, XRP(100), USD(501)));
724 BEAST_EXPECT(expectOffers(env, carol, 1));
725 env(delegate::set(gw, bob, {"PaymentMint"}));
726 env.close();
727
728 // bob can not send cross currency payment on behalf of the gw,
729 // even with PaymentMint permission and gw being the issuer.
730 env(pay(gw, alice, USD(5000)),
731 sendmax(XRP(1001)),
733 delegate::as(bob),
735 BEAST_EXPECT(expectOffers(env, carol, 1));
736
737 env(pay(gw, alice, USD(5000)),
738 path(~XRP),
740 delegate::as(bob),
742 BEAST_EXPECT(expectOffers(env, carol, 1));
743
744 // succeed with direct payment
745 env(pay(gw, alice, USD(100)), delegate::as(bob));
746 env.close();
747 }
748
749 // PaymentBurn
750 {
751 env(offer(bob, XRP(100), USD(501)));
752 BEAST_EXPECT(expectOffers(env, bob, 1));
753 env(delegate::set(alice, bob, {"PaymentBurn"}));
754 env.close();
755
756 // bob can not send cross currency payment on behalf of alice,
757 // even with PaymentBurn permission and gw being the issuer.
758 env(pay(alice, gw, USD(5000)),
759 sendmax(XRP(1001)),
761 delegate::as(bob),
763 BEAST_EXPECT(expectOffers(env, bob, 1));
764
765 env(pay(alice, gw, USD(5000)),
766 path(~XRP),
768 delegate::as(bob),
770 BEAST_EXPECT(expectOffers(env, bob, 1));
771
772 // succeed with direct payment
773 env(pay(alice, gw, USD(100)), delegate::as(bob));
774 env.close();
775 }
776 }
777
778 // PaymentMint and PaymentBurn for MPT
779 {
780 std::string logs;
781 Env env(*this, features, std::make_unique<CaptureLogs>(&logs));
782 Account const alice{"alice"};
783 Account const bob{"bob"};
784 Account const gw{"gateway"};
785
786 MPTTester mpt(env, gw, {.holders = {alice, bob}});
787 mpt.create(
788 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
789
790 mpt.authorize({.account = alice});
791 mpt.authorize({.account = bob});
792
793 auto const MPT = mpt["MPT"];
794 env(pay(gw, alice, MPT(500)));
795 env(pay(gw, bob, MPT(500)));
796 env.close();
797 auto aliceMPT = env.balance(alice, MPT);
798 auto bobMPT = env.balance(bob, MPT);
799
800 // PaymentMint
801 {
802 env(delegate::set(gw, bob, {"PaymentMint"}));
803 env.close();
804
805 env(pay(gw, alice, MPT(50)), delegate::as(bob));
806 BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT + MPT(50));
807 BEAST_EXPECT(env.balance(bob, MPT) == bobMPT);
808 aliceMPT = env.balance(alice, MPT);
809 }
810
811 // PaymentBurn
812 {
813 env(delegate::set(alice, bob, {"PaymentBurn"}));
814 env.close();
815
816 env(pay(alice, gw, MPT(50)), delegate::as(bob));
817 BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT - MPT(50));
818 BEAST_EXPECT(env.balance(bob, MPT) == bobMPT);
819 aliceMPT = env.balance(alice, MPT);
820 }
821
822 // Grant both granular permissions and tx level permission.
823 {
824 env(delegate::set(
825 alice, bob, {"PaymentBurn", "PaymentMint", "Payment"}));
826 env.close();
827 env(pay(alice, gw, MPT(50)), delegate::as(bob));
828 BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT - MPT(50));
829 BEAST_EXPECT(env.balance(bob, MPT) == bobMPT);
830 aliceMPT = env.balance(alice, MPT);
831 env(pay(alice, bob, MPT(100)), delegate::as(bob));
832 BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT - MPT(100));
833 BEAST_EXPECT(env.balance(bob, MPT) == bobMPT + MPT(100));
834 }
835 }
836 }
837
838 void
840 {
841 testcase("test TrustSet granular permissions");
842 using namespace jtx;
843
844 // test TrustlineUnfreeze, TrustlineFreeze and TrustlineAuthorize
845 {
846 Env env(*this);
847 Account gw{"gw"};
848 Account alice{"alice"};
849 Account bob{"bob"};
850 env.fund(XRP(10000), gw, alice, bob);
851 env(fset(gw, asfRequireAuth));
852 env.close();
853
854 env(delegate::set(alice, bob, {"TrustlineUnfreeze"}));
855 env.close();
856 // bob can not create trustline on behalf of alice because he only
857 // has unfreeze permission
858 env(trust(alice, gw["USD"](50)),
859 delegate::as(bob),
861 env.close();
862
863 // alice creates trustline by herself
864 env(trust(alice, gw["USD"](50)));
865 env.close();
866
867 // gw gives bob unfreeze permission
868 env(delegate::set(gw, bob, {"TrustlineUnfreeze"}));
869 env.close();
870
871 // unsupported flags
872 env(trust(alice, gw["USD"](50), tfSetNoRipple),
873 delegate::as(bob),
875 env(trust(alice, gw["USD"](50), tfClearNoRipple),
876 delegate::as(bob),
878 env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze),
879 delegate::as(bob),
881 env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze),
882 delegate::as(bob),
884 env.close();
885
886 // supported flags with wrong permission
887 env(trust(gw, gw["USD"](0), alice, tfSetfAuth),
888 delegate::as(bob),
890 env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
891 delegate::as(bob),
893 env.close();
894
895 env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
896 env.close();
897 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
898 delegate::as(bob),
900 env.close();
901 // although trustline authorize is granted, bob can not change the
902 // limit number
903 env(trust(gw, gw["USD"](50), alice, tfSetfAuth),
904 delegate::as(bob),
906 env.close();
907
908 // supported flags with correct permission
909 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::as(bob));
910 env.close();
911 env(delegate::set(
912 gw, bob, {"TrustlineAuthorize", "TrustlineFreeze"}));
913 env.close();
914 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
915 env.close();
916 env(delegate::set(
917 gw, bob, {"TrustlineAuthorize", "TrustlineUnfreeze"}));
918 env.close();
919 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
920 delegate::as(bob));
921 env.close();
922 // but bob can not freeze trustline because he no longer has freeze
923 // permission
924 env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
925 delegate::as(bob),
927
928 // cannot update LimitAmount with granular permission, both high and
929 // low account
930 env(trust(alice, gw["USD"](100)),
931 delegate::as(bob),
933 env(trust(gw, alice["USD"](100)),
934 delegate::as(bob),
936
937 // can not set QualityIn or QualityOut
938 auto tx = trust(alice, gw["USD"](50));
939 tx["QualityIn"] = "1000";
941 auto tx2 = trust(alice, gw["USD"](50));
942 tx2["QualityOut"] = "1000";
944 auto tx3 = trust(gw, alice["USD"](50));
945 tx3["QualityIn"] = "1000";
947 auto tx4 = trust(gw, alice["USD"](50));
948 tx4["QualityOut"] = "1000";
950
951 // granting TrustSet can make it work
952 env(delegate::set(gw, bob, {"TrustSet"}));
953 env.close();
954 auto tx5 = trust(gw, alice["USD"](50));
955 tx5["QualityOut"] = "1000";
956 env(tx5, delegate::as(bob));
957 auto tx6 = trust(alice, gw["USD"](50));
958 tx6["QualityOut"] = "1000";
960 env(delegate::set(alice, bob, {"TrustSet"}));
961 env.close();
962 env(tx6, delegate::as(bob));
963 }
964
965 // test mix of transaction level delegation and granular delegation
966 {
967 Env env(*this);
968 Account gw{"gw"};
969 Account alice{"alice"};
970 Account bob{"bob"};
971 env.fund(XRP(10000), gw, alice, bob);
972 env(fset(gw, asfRequireAuth));
973 env.close();
974
975 // bob does not have permission
976 env(trust(alice, gw["USD"](50)),
977 delegate::as(bob),
979 env(delegate::set(
980 alice, bob, {"TrustlineUnfreeze", "NFTokenCreateOffer"}));
981 env.close();
982 // bob still does not have permission
983 env(trust(alice, gw["USD"](50)),
984 delegate::as(bob),
986
987 // add TrustSet permission and some unrelated permission
988 env(delegate::set(
989 alice,
990 bob,
991 {"TrustlineUnfreeze",
992 "NFTokenCreateOffer",
993 "TrustSet",
994 "AccountTransferRateSet"}));
995 env.close();
996 env(trust(alice, gw["USD"](50)), delegate::as(bob));
997 env.close();
998
999 env(delegate::set(
1000 gw,
1001 bob,
1002 {"TrustlineUnfreeze",
1003 "NFTokenCreateOffer",
1004 "TrustSet",
1005 "AccountTransferRateSet"}));
1006 env.close();
1007
1008 // since bob has TrustSet permission, he does not need
1009 // TrustlineFreeze granular permission to freeze the trustline
1010 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
1011 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
1012 delegate::as(bob));
1013 // bob can perform all the operations regarding TrustSet
1014 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob));
1015 env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze),
1016 delegate::as(bob));
1017 env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze),
1018 delegate::as(bob));
1019 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::as(bob));
1020 env(trust(alice, gw["USD"](50), tfSetNoRipple), delegate::as(bob));
1021 env(trust(alice, gw["USD"](50), tfClearNoRipple),
1022 delegate::as(bob));
1023 }
1024
1025 // tfFullyCanonicalSig won't block delegated transaction
1026 {
1027 Env env(*this);
1028 Account gw{"gw"};
1029 Account alice{"alice"};
1030 Account bob{"bob"};
1031 env.fund(XRP(10000), gw, alice, bob);
1032 env(fset(gw, asfRequireAuth));
1033 env.close();
1034 env(trust(alice, gw["USD"](50)));
1035 env.close();
1036
1037 env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
1038 env.close();
1039 env(trust(
1040 gw, gw["USD"](0), alice, tfSetfAuth | tfFullyCanonicalSig),
1041 delegate::as(bob));
1042 }
1043 }
1044
1045 void
1047 {
1048 testcase("test AccountSet granular permissions");
1049 using namespace jtx;
1050
1051 // test AccountDomainSet, AccountEmailHashSet,
1052 // AccountMessageKeySet,AccountTransferRateSet, and AccountTickSizeSet
1053 // granular permissions
1054 {
1055 Env env(*this);
1056 auto const alice = Account{"alice"};
1057 auto const bob = Account{"bob"};
1058 env.fund(XRP(10000), alice, bob);
1059 env.close();
1060
1061 // alice gives bob some random permission, which is not related to
1062 // the AccountSet transaction
1063 env(delegate::set(alice, bob, {"TrustlineUnfreeze"}));
1064 env.close();
1065
1066 // bob does not have permission to set domain
1067 // on behalf of alice
1068 std::string const domain = "example.com";
1069 auto jt = noop(alice);
1070 jt[sfDomain] = strHex(domain);
1071 jt[sfDelegate] = bob.human();
1072
1073 // add granular permission related to AccountSet but is not the
1074 // correct permission for domain set
1075 env(delegate::set(
1076 alice, bob, {"TrustlineUnfreeze", "AccountEmailHashSet"}));
1077 env.close();
1079
1080 // alice give granular permission of AccountDomainSet to bob
1081 env(delegate::set(alice, bob, {"AccountDomainSet"}));
1082 env.close();
1083
1084 // bob set account domain on behalf of alice
1085 env(jt);
1086 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
1087
1088 // bob can reset domain
1089 jt[sfDomain] = "";
1090 env(jt);
1091 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfDomain));
1092
1093 // bob tries to set unauthorized flag, it will fail
1094 std::string const failDomain = "fail_domain_update";
1095 jt[sfFlags] = tfRequireAuth;
1096 jt[sfDomain] = strHex(failDomain);
1098 // reset flag number
1099 jt[sfFlags] = 0;
1100
1101 // bob tries to update domain and set email hash,
1102 // but he does not have permission to set email hash
1103 jt[sfDomain] = strHex(domain);
1104 std::string const mh("5F31A79367DC3137FADA860C05742EE6");
1105 jt[sfEmailHash] = mh;
1107
1108 // alice give granular permission of AccountEmailHashSet to bob
1109 env(delegate::set(
1110 alice, bob, {"AccountDomainSet", "AccountEmailHashSet"}));
1111 env.close();
1112 env(jt);
1113 BEAST_EXPECT(to_string((*env.le(alice))[sfEmailHash]) == mh);
1114 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
1115
1116 // bob does not have permission to set message key for alice
1117 auto const rkp = randomKeyPair(KeyType::ed25519);
1118 jt[sfMessageKey] = strHex(rkp.first.slice());
1120
1121 // alice give granular permission of AccountMessageKeySet to bob
1122 env(delegate::set(
1123 alice,
1124 bob,
1125 {"AccountDomainSet",
1126 "AccountEmailHashSet",
1127 "AccountMessageKeySet"}));
1128 env.close();
1129
1130 // bob can set message key for alice
1131 env(jt);
1132 BEAST_EXPECT(
1133 strHex((*env.le(alice))[sfMessageKey]) ==
1134 strHex(rkp.first.slice()));
1135 jt[sfMessageKey] = "";
1136 env(jt);
1137 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfMessageKey));
1138
1139 // bob does not have permission to set transfer rate for alice
1140 env(rate(alice, 2.0),
1141 delegate::as(bob),
1143
1144 // alice give granular permission of AccountTransferRateSet to bob
1145 env(delegate::set(
1146 alice,
1147 bob,
1148 {"AccountDomainSet",
1149 "AccountEmailHashSet",
1150 "AccountMessageKeySet",
1151 "AccountTransferRateSet"}));
1152 env.close();
1153 auto jtRate = rate(alice, 2.0);
1154 jtRate[sfDelegate] = bob.human();
1155 env(jtRate, delegate::as(bob));
1156 BEAST_EXPECT((*env.le(alice))[sfTransferRate] == 2000000000);
1157
1158 // bob does not have permission to set ticksize for alice
1159 jt[sfTickSize] = 8;
1161
1162 // alice give granular permission of AccountTickSizeSet to bob
1163 env(delegate::set(
1164 alice,
1165 bob,
1166 {"AccountDomainSet",
1167 "AccountEmailHashSet",
1168 "AccountMessageKeySet",
1169 "AccountTransferRateSet",
1170 "AccountTickSizeSet"}));
1171 env.close();
1172 env(jt);
1173 BEAST_EXPECT((*env.le(alice))[sfTickSize] == 8);
1174
1175 // can not set asfRequireAuth flag for alice
1176 env(fset(alice, asfRequireAuth),
1177 delegate::as(bob),
1179
1180 // reset Delegate will delete the Delegate
1181 // object
1182 env(delegate::set(alice, bob, {}));
1183 // bib still does not have permission to set asfRequireAuth for
1184 // alice
1185 env(fset(alice, asfRequireAuth),
1186 delegate::as(bob),
1188 // alice can set for herself
1189 env(fset(alice, asfRequireAuth));
1190 env.require(flags(alice, asfRequireAuth));
1191 env.close();
1192
1193 // can not update tick size because bob no longer has permission
1194 jt[sfTickSize] = 7;
1196
1197 env(delegate::set(
1198 alice,
1199 bob,
1200 {"AccountDomainSet",
1201 "AccountEmailHashSet",
1202 "AccountMessageKeySet"}));
1203 env.close();
1204
1205 // bob does not have permission to set wallet locater for alice
1206 std::string const locator =
1207 "9633EC8AF54F16B5286DB1D7B519EF49EEFC050C0C8AC4384F1D88ACD1BFDF"
1208 "05";
1209 auto jv2 = noop(alice);
1210 jv2[sfDomain] = strHex(domain);
1211 jv2[sfDelegate] = bob.human();
1212 jv2[sfWalletLocator] = locator;
1213 env(jv2, ter(terNO_DELEGATE_PERMISSION));
1214 }
1215
1216 // can not set AccountSet flags on behalf of other account
1217 {
1218 Env env(*this);
1219 auto const alice = Account{"alice"};
1220 auto const bob = Account{"bob"};
1221 env.fund(XRP(10000), alice, bob);
1222 env.close();
1223
1224 auto testSetClearFlag = [&](std::uint32_t flag) {
1225 // bob can not set flag on behalf of alice
1226 env(fset(alice, flag),
1227 delegate::as(bob),
1229 // alice set by herself
1230 env(fset(alice, flag));
1231 env.close();
1232 env.require(flags(alice, flag));
1233 // bob can not clear on behalf of alice
1234 env(fclear(alice, flag),
1235 delegate::as(bob),
1237 };
1238
1239 // testSetClearFlag(asfNoFreeze);
1240 testSetClearFlag(asfRequireAuth);
1241 testSetClearFlag(asfAllowTrustLineClawback);
1242
1243 // alice gives some granular permissions to bob
1244 env(delegate::set(
1245 alice,
1246 bob,
1247 {"AccountDomainSet",
1248 "AccountEmailHashSet",
1249 "AccountMessageKeySet"}));
1250 env.close();
1251
1252 testSetClearFlag(asfDefaultRipple);
1253 testSetClearFlag(asfDepositAuth);
1254 testSetClearFlag(asfDisallowIncomingCheck);
1255 testSetClearFlag(asfDisallowIncomingNFTokenOffer);
1256 testSetClearFlag(asfDisallowIncomingPayChan);
1257 testSetClearFlag(asfDisallowIncomingTrustline);
1258 testSetClearFlag(asfDisallowXRP);
1259 testSetClearFlag(asfRequireDest);
1260 testSetClearFlag(asfGlobalFreeze);
1261
1262 // bob can not set asfAccountTxnID on behalf of alice
1263 env(fset(alice, asfAccountTxnID),
1264 delegate::as(bob),
1266 env(fset(alice, asfAccountTxnID));
1267 env.close();
1268 BEAST_EXPECT(env.le(alice)->isFieldPresent(sfAccountTxnID));
1269 env(fclear(alice, asfAccountTxnID),
1270 delegate::as(bob),
1272
1273 // bob can not set asfAuthorizedNFTokenMinter on behalf of alice
1275 jt[sfDelegate] = bob.human();
1276 jt[sfNFTokenMinter] = bob.human();
1278
1279 // bob gives alice some permissions
1280 env(delegate::set(
1281 bob,
1282 alice,
1283 {"AccountDomainSet",
1284 "AccountEmailHashSet",
1285 "AccountMessageKeySet"}));
1286 env.close();
1287
1288 // since we can not set asfNoFreeze if asfAllowTrustLineClawback is
1289 // set, which can not be clear either. Test alice set asfNoFreeze on
1290 // behalf of bob.
1291 env(fset(alice, asfNoFreeze),
1292 delegate::as(bob),
1294 env(fset(bob, asfNoFreeze));
1295 env.close();
1296 env.require(flags(bob, asfNoFreeze));
1297 // alice can not clear on behalf of bob
1298 env(fclear(alice, asfNoFreeze),
1299 delegate::as(bob),
1301
1302 // bob can not set asfDisableMaster on behalf of alice
1303 Account const bobKey{"bobKey", KeyType::secp256k1};
1304 env(regkey(bob, bobKey));
1305 env.close();
1306 env(fset(alice, asfDisableMaster),
1307 delegate::as(bob),
1308 sig(bob),
1310 }
1311
1312 // tfFullyCanonicalSig won't block delegated transaction
1313 {
1314 Env env(*this);
1315 Account alice{"alice"};
1316 Account bob{"bob"};
1317 env.fund(XRP(10000), alice, bob);
1318 env.close();
1319
1320 env(delegate::set(
1321 alice, bob, {"AccountDomainSet", "AccountEmailHashSet"}));
1322 env.close();
1323
1324 std::string const domain = "example.com";
1325 auto jt = noop(alice);
1326 jt[sfDomain] = strHex(domain);
1327 jt[sfDelegate] = bob.human();
1328 jt[sfFlags] = tfFullyCanonicalSig;
1329
1330 env(jt);
1331 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
1332 }
1333 }
1334
1335 void
1337 {
1338 testcase("test MPTokenIssuanceSet granular");
1339 using namespace jtx;
1340
1341 // test MPTokenIssuanceUnlock and MPTokenIssuanceLock permissions
1342 {
1343 Env env(*this);
1344 Account alice{"alice"};
1345 Account bob{"bob"};
1346 env.fund(XRP(100000), alice, bob);
1347 env.close();
1348
1349 MPTTester mpt(env, alice, {.fund = false});
1350 env.close();
1351 mpt.create({.flags = tfMPTCanLock});
1352 env.close();
1353
1354 // delegate ledger object is not created yet
1355 mpt.set(
1356 {.account = alice,
1357 .flags = tfMPTLock,
1358 .delegate = bob,
1360
1361 // alice gives granular permission to bob of MPTokenIssuanceUnlock
1362 env(delegate::set(alice, bob, {"MPTokenIssuanceUnlock"}));
1363 env.close();
1364 // bob does not have lock permission
1365 mpt.set(
1366 {.account = alice,
1367 .flags = tfMPTLock,
1368 .delegate = bob,
1370 // bob now has lock permission, but does not have unlock permission
1371 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
1372 env.close();
1373 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1374 mpt.set(
1375 {.account = alice,
1376 .flags = tfMPTUnlock,
1377 .delegate = bob,
1379
1380 // now bob can lock and unlock
1381 env(delegate::set(
1382 alice, bob, {"MPTokenIssuanceLock", "MPTokenIssuanceUnlock"}));
1383 env.close();
1384 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1385 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1386 env.close();
1387 }
1388
1389 // test mix of granular and transaction level permission
1390 {
1391 Env env(*this);
1392 Account alice{"alice"};
1393 Account bob{"bob"};
1394 env.fund(XRP(100000), alice, bob);
1395 env.close();
1396
1397 MPTTester mpt(env, alice, {.fund = false});
1398 env.close();
1399 mpt.create({.flags = tfMPTCanLock});
1400 env.close();
1401
1402 // alice gives granular permission to bob of MPTokenIssuanceLock
1403 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
1404 env.close();
1405 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1406 // bob does not have unlock permission
1407 mpt.set(
1408 {.account = alice,
1409 .flags = tfMPTUnlock,
1410 .delegate = bob,
1412
1413 // alice gives bob some unrelated permission with
1414 // MPTokenIssuanceLock
1415 env(delegate::set(
1416 alice,
1417 bob,
1418 {"NFTokenMint", "MPTokenIssuanceLock", "NFTokenBurn"}));
1419 env.close();
1420 // bob can not unlock
1421 mpt.set(
1422 {.account = alice,
1423 .flags = tfMPTUnlock,
1424 .delegate = bob,
1426
1427 // alice add MPTokenIssuanceSet to permissions
1428 env(delegate::set(
1429 alice,
1430 bob,
1431 {"NFTokenMint",
1432 "MPTokenIssuanceLock",
1433 "NFTokenBurn",
1434 "MPTokenIssuanceSet"}));
1435 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1436 // alice can lock by herself
1437 mpt.set({.account = alice, .flags = tfMPTLock});
1438 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
1439 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
1440 }
1441
1442 // tfFullyCanonicalSig won't block delegated transaction
1443 {
1444 Env env(*this);
1445 Account alice{"alice"};
1446 Account bob{"bob"};
1447 env.fund(XRP(100000), alice, bob);
1448 env.close();
1449
1450 MPTTester mpt(env, alice, {.fund = false});
1451 env.close();
1452 mpt.create({.flags = tfMPTCanLock});
1453 env.close();
1454
1455 // alice gives granular permission to bob of MPTokenIssuanceLock
1456 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
1457 env.close();
1458 mpt.set(
1459 {.account = alice,
1460 .flags = tfMPTLock | tfFullyCanonicalSig,
1461 .delegate = bob});
1462 }
1463 }
1464
1465 void
1467 {
1468 testcase("test single sign");
1469 using namespace jtx;
1470
1471 Env env(*this);
1472 Account alice{"alice"};
1473 Account bob{"bob"};
1474 Account carol{"carol"};
1475 env.fund(XRP(100000), alice, bob, carol);
1476 env.close();
1477
1478 env(delegate::set(alice, bob, {"Payment"}));
1479 env.close();
1480
1481 auto aliceBalance = env.balance(alice);
1482 auto bobBalance = env.balance(bob);
1483 auto carolBalance = env.balance(carol);
1484
1485 env(pay(alice, carol, XRP(100)),
1486 fee(XRP(10)),
1487 delegate::as(bob),
1488 sig(bob));
1489 env.close();
1490 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100));
1491 BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10));
1492 BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(100));
1493 }
1494
1495 void
1497 {
1498 testcase("test single sign with bad secret");
1499 using namespace jtx;
1500
1501 {
1502 Env env(*this);
1503 Account alice{"alice"};
1504 Account bob{"bob"};
1505 Account carol{"carol"};
1506 env.fund(XRP(100000), alice, bob, carol);
1507 env.close();
1508
1509 env(delegate::set(alice, bob, {"Payment"}));
1510 env.close();
1511
1512 auto aliceBalance = env.balance(alice);
1513 auto bobBalance = env.balance(bob);
1514 auto carolBalance = env.balance(carol);
1515
1516 env(pay(alice, carol, XRP(100)),
1517 fee(XRP(10)),
1518 delegate::as(bob),
1519 sig(alice),
1520 ter(tefBAD_AUTH));
1521 env.close();
1522 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1523 BEAST_EXPECT(env.balance(bob) == bobBalance);
1524 BEAST_EXPECT(env.balance(carol) == carolBalance);
1525 }
1526
1527 {
1528 Env env(*this);
1529 Account alice{"alice"}, bob{"bob"}, carol{"carol"};
1530 env.fund(XRP(100000), alice, bob, carol);
1531 env.close();
1532
1533 env(delegate::set(alice, bob, {"TrustSet"}));
1534 env.close();
1535
1536 auto aliceBalance = env.balance(alice);
1537 auto bobBalance = env.balance(bob);
1538 auto carolBalance = env.balance(carol);
1539
1540 env(pay(alice, carol, XRP(100)),
1541 fee(XRP(10)),
1542 delegate::as(bob),
1543 sig(carol),
1545 env.close();
1546 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1547 BEAST_EXPECT(env.balance(bob) == bobBalance);
1548 BEAST_EXPECT(env.balance(carol) == carolBalance);
1549
1550 env(pay(alice, carol, XRP(100)),
1551 fee(XRP(10)),
1552 delegate::as(bob),
1553 sig(alice),
1555 env.close();
1556 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1557 BEAST_EXPECT(env.balance(bob) == bobBalance);
1558 BEAST_EXPECT(env.balance(carol) == carolBalance);
1559 }
1560
1561 {
1562 Env env(*this);
1563 Account alice{"alice"}, bob{"bob"}, carol{"carol"};
1564 env.fund(XRP(100000), alice, bob, carol);
1565 env.close();
1566
1567 auto aliceBalance = env.balance(alice);
1568 auto bobBalance = env.balance(bob);
1569 auto carolBalance = env.balance(carol);
1570
1571 env(pay(alice, carol, XRP(100)),
1572 fee(XRP(10)),
1573 delegate::as(bob),
1574 sig(alice),
1576 env.close();
1577 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1578 BEAST_EXPECT(env.balance(bob) == bobBalance);
1579 BEAST_EXPECT(env.balance(carol) == carolBalance);
1580
1581 env(pay(alice, carol, XRP(100)),
1582 fee(XRP(10)),
1583 delegate::as(bob),
1584 sig(carol),
1586 env.close();
1587 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1588 BEAST_EXPECT(env.balance(bob) == bobBalance);
1589 BEAST_EXPECT(env.balance(carol) == carolBalance);
1590 }
1591 }
1592
1593 void
1595 {
1596 testcase("test multi sign");
1597 using namespace jtx;
1598
1599 Env env(*this);
1600 Account alice{"alice"};
1601 Account bob{"bob"};
1602 Account carol{"carol"};
1603 Account daria{"daria"};
1604 Account edward{"edward"};
1605 env.fund(XRP(100000), alice, bob, carol, daria, edward);
1606 env.close();
1607
1608 env(signers(bob, 2, {{daria, 1}, {edward, 1}}));
1609 env.close();
1610
1611 env(delegate::set(alice, bob, {"Payment"}));
1612 env.close();
1613
1614 auto aliceBalance = env.balance(alice);
1615 auto bobBalance = env.balance(bob);
1616 auto carolBalance = env.balance(carol);
1617 auto dariaBalance = env.balance(daria);
1618 auto edwardBalance = env.balance(edward);
1619
1620 env(pay(alice, carol, XRP(100)),
1621 fee(XRP(10)),
1622 delegate::as(bob),
1623 msig(daria, edward));
1624 env.close();
1625 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100));
1626 BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10));
1627 BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(100));
1628 BEAST_EXPECT(env.balance(daria) == dariaBalance);
1629 BEAST_EXPECT(env.balance(edward) == edwardBalance);
1630 }
1631
1632 void
1634 {
1635 testcase("test multi sign which does not meet quorum");
1636 using namespace jtx;
1637
1638 Env env(*this);
1639 Account alice{"alice"};
1640 Account bob{"bob"};
1641 Account carol{"carol"};
1642 Account daria = Account{"daria"};
1643 Account edward = Account{"edward"};
1644 Account fred = Account{"fred"};
1645 env.fund(XRP(100000), alice, bob, carol, daria, edward, fred);
1646 env.close();
1647
1648 env(signers(bob, 3, {{daria, 1}, {edward, 1}, {fred, 1}}));
1649 env.close();
1650
1651 env(delegate::set(alice, bob, {"Payment"}));
1652 env.close();
1653
1654 auto aliceBalance = env.balance(alice);
1655 auto bobBalance = env.balance(bob);
1656 auto carolBalance = env.balance(carol);
1657 auto dariaBalance = env.balance(daria);
1658 auto edwardBalance = env.balance(edward);
1659
1660 env(pay(alice, carol, XRP(100)),
1661 fee(XRP(10)),
1662 delegate::as(bob),
1663 msig(daria, edward),
1665 env.close();
1666 BEAST_EXPECT(env.balance(alice) == aliceBalance);
1667 BEAST_EXPECT(env.balance(bob) == bobBalance);
1668 BEAST_EXPECT(env.balance(carol) == carolBalance);
1669 BEAST_EXPECT(env.balance(daria) == dariaBalance);
1670 BEAST_EXPECT(env.balance(edward) == edwardBalance);
1671 }
1672
1673 void
1675 {
1676 testcase("test permission value");
1677 using namespace jtx;
1678
1679 Env env(*this, features);
1680
1681 Account alice{"alice"};
1682 Account bob{"bob"};
1683 env.fund(XRP(100000), alice, bob);
1684 env.close();
1685
1686 auto buildRequest = [&](auto value) -> Json::Value {
1687 Json::Value jv;
1688 jv[jss::TransactionType] = jss::DelegateSet;
1689 jv[jss::Account] = alice.human();
1690 jv[sfAuthorize.jsonName] = bob.human();
1691
1692 Json::Value permissionsJson(Json::arrayValue);
1693 Json::Value permissionValue;
1694 permissionValue[sfPermissionValue.jsonName] = value;
1695 Json::Value permissionObj;
1696 permissionObj[sfPermission.jsonName] = permissionValue;
1697 permissionsJson.append(permissionObj);
1698 jv[sfPermissions.jsonName] = permissionsJson;
1699
1700 return jv;
1701 };
1702
1703 // invalid permission value.
1704 // neither granular permission nor transaction level permission
1705 for (auto value : {0, 100000, 54321})
1706 {
1707 auto jv = buildRequest(value);
1708 env(jv, ter(temMALFORMED));
1709 }
1710 }
1711
1712 void
1714 {
1715 testcase("test delegate disabled tx");
1716 using namespace jtx;
1717
1718 // map of tx and required feature.
1719 // non-delegatable tx are not included.
1720 // NFTokenMint, NFTokenBurn, NFTokenCreateOffer, NFTokenCancelOffer,
1721 // NFTokenAcceptOffer are not included, they are tested separately.
1723 {"TicketCreate", featureTicketBatch},
1724 {"CheckCreate", featureChecks},
1725 {"CheckCash", featureChecks},
1726 {"CheckCancel", featureChecks},
1727 {"DepositPreauth", featureDepositPreauth},
1728 {"Clawback", featureClawback},
1729 {"AMMClawback", featureAMMClawback},
1730 {"AMMCreate", featureAMM},
1731 {"AMMDeposit", featureAMM},
1732 {"AMMWithdraw", featureAMM},
1733 {"AMMVote", featureAMM},
1734 {"AMMBid", featureAMM},
1735 {"AMMDelete", featureAMM},
1736 {"XChainCreateClaimID", featureXChainBridge},
1737 {"XChainCommit", featureXChainBridge},
1738 {"XChainClaim", featureXChainBridge},
1739 {"XChainAccountCreateCommit", featureXChainBridge},
1740 {"XChainAddClaimAttestation", featureXChainBridge},
1741 {"XChainAddAccountCreateAttestation", featureXChainBridge},
1742 {"XChainModifyBridge", featureXChainBridge},
1743 {"XChainCreateBridge", featureXChainBridge},
1744 {"DIDSet", featureDID},
1745 {"DIDDelete", featureDID},
1746 {"OracleSet", featurePriceOracle},
1747 {"OracleDelete", featurePriceOracle},
1748 {"LedgerStateFix", fixNFTokenPageLinks},
1749 {"MPTokenIssuanceCreate", featureMPTokensV1},
1750 {"MPTokenIssuanceDestroy", featureMPTokensV1},
1751 {"MPTokenIssuanceSet", featureMPTokensV1},
1752 {"MPTokenAuthorize", featureMPTokensV1},
1753 {"CredentialCreate", featureCredentials},
1754 {"CredentialAccept", featureCredentials},
1755 {"CredentialDelete", featureCredentials},
1756 {"NFTokenModify", featureDynamicNFT},
1757 {"PermissionedDomainSet", featurePermissionedDomains},
1758 {"PermissionedDomainDelete", featurePermissionedDomains},
1759 {"VaultCreate", featureSingleAssetVault},
1760 {"VaultSet", featureSingleAssetVault},
1761 {"VaultDelete", featureSingleAssetVault},
1762 {"VaultDeposit", featureSingleAssetVault},
1763 {"VaultWithdraw", featureSingleAssetVault},
1764 {"VaultClawback", featureSingleAssetVault}};
1765
1766 // Can not delegate tx if any required feature disabled.
1767 {
1768 auto txAmendmentDisabled = [&](FeatureBitset features,
1769 std::string const& tx) {
1770 BEAST_EXPECT(txRequiredFeatures.contains(tx));
1771
1772 Env env(*this, features - txRequiredFeatures[tx]);
1773
1774 Account const alice{"alice"};
1775 Account const bob{"bob"};
1776 env.fund(XRP(100000), alice, bob);
1777 env.close();
1778
1779 env(delegate::set(alice, bob, {tx}), ter(temMALFORMED));
1780 };
1781
1782 for (auto const& tx : txRequiredFeatures)
1783 txAmendmentDisabled(features, tx.first);
1784 }
1785
1786 // if all the required features in txRequiredFeatures are enabled, will
1787 // succeed
1788 {
1789 auto txAmendmentEnabled = [&](std::string const& tx) {
1790 Env env(*this, features);
1791
1792 Account const alice{"alice"};
1793 Account const bob{"bob"};
1794 env.fund(XRP(100000), alice, bob);
1795 env.close();
1796
1797 env(delegate::set(alice, bob, {tx}));
1798 };
1799
1800 for (auto const& tx : txRequiredFeatures)
1801 txAmendmentEnabled(tx.first);
1802 }
1803
1804 // NFTokenMint, NFTokenBurn, NFTokenCreateOffer, NFTokenCancelOffer, and
1805 // NFTokenAcceptOffer are tested separately. Since
1806 // featureNonFungibleTokensV1_1 includes the functionality of
1807 // featureNonFungibleTokensV1, fixNFTokenNegOffer, and fixNFTokenDirV1,
1808 // both featureNonFungibleTokensV1_1 and featureNonFungibleTokensV1 need
1809 // to be disabled to block these transactions from being delegated.
1810 {
1811 Env env(
1812 *this,
1813 features - featureNonFungibleTokensV1 -
1814 featureNonFungibleTokensV1_1);
1815
1816 Account const alice{"alice"};
1817 Account const bob{"bob"};
1818 env.fund(XRP(100000), alice, bob);
1819 env.close();
1820
1821 for (auto const tx :
1822 {"NFTokenMint",
1823 "NFTokenBurn",
1824 "NFTokenCreateOffer",
1825 "NFTokenCancelOffer",
1826 "NFTokenAcceptOffer"})
1827 {
1828 env(delegate::set(alice, bob, {tx}), ter(temMALFORMED));
1829 }
1830 }
1831
1832 // NFTokenMint, NFTokenBurn, NFTokenCreateOffer, NFTokenCancelOffer, and
1833 // NFTokenAcceptOffer are allowed to be delegated if either
1834 // featureNonFungibleTokensV1 or featureNonFungibleTokensV1_1 is
1835 // enabled.
1836 {
1837 for (auto const feature :
1838 {featureNonFungibleTokensV1, featureNonFungibleTokensV1_1})
1839 {
1840 Env env(*this, features - feature);
1841 Account const alice{"alice"};
1842 Account const bob{"bob"};
1843 env.fund(XRP(100000), alice, bob);
1844 env.close();
1845
1846 for (auto const tx :
1847 {"NFTokenMint",
1848 "NFTokenBurn",
1849 "NFTokenCreateOffer",
1850 "NFTokenCancelOffer",
1851 "NFTokenAcceptOffer"})
1852 env(delegate::set(alice, bob, {tx}));
1853 }
1854 }
1855 }
1856
1857 void
1882};
1883BEAST_DEFINE_TESTSUITE(Delegate, app, ripple);
1884} // namespace test
1885} // 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.
void testPermissionValue(FeatureBitset features)
void testFeatureDisabled(FeatureBitset features)
void testTxReqireFeatures(FeatureBitset features)
void testInvalidRequest(FeatureBitset features)
void testPaymentGranular(FeatureBitset features)
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:116
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:269
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:547
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:122
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:321
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:290
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:184
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:278
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:87
Converts to MPT Issue or STAmount.
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
Add a path.
Definition paths.h:58
Sets the SendMax on a JTx.
Definition sendmax.h:33
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
Set the flags on a JTx.
Definition txflags.h:31
T is_same_v
@ 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:121
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
bool expectOffers(Env &env, AccountID const &account, std::uint16_t size, std::vector< Amounts > const &toMatch)
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 offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:29
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:111
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 const tfMPTCanTransfer
Definition TxFlags.h:152
constexpr std::uint32_t asfRequireDest
Definition TxFlags.h:77
constexpr std::uint32_t const tfMPTUnlock
Definition TxFlags.h:177
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 tfPartialPayment
Definition TxFlags.h:108
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_TARGET
Definition TER.h:305
@ tecUNFUNDED_PAYMENT
Definition TER.h:286
@ tecINSUFFICIENT_RESERVE
Definition TER.h:308
constexpr std::uint32_t const tfMPTLock
Definition TxFlags.h:176
@ tesSUCCESS
Definition TER.h:245
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
@ terNO_DELEGATE_PERMISSION
Definition TER.h:230
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:148
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