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