xrpld
Loading...
Searching...
No Matches
Delegate_test.cpp
1#include <test/jtx/Account.h>
2#include <test/jtx/CaptureLogs.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/acctdelete.h>
6#include <test/jtx/amount.h>
7#include <test/jtx/balance.h>
8#include <test/jtx/batch.h>
9#include <test/jtx/delegate.h>
10#include <test/jtx/delivermin.h>
11#include <test/jtx/did.h>
12#include <test/jtx/domain.h>
13#include <test/jtx/fee.h>
14#include <test/jtx/flags.h>
15#include <test/jtx/mpt.h>
16#include <test/jtx/multisign.h>
17#include <test/jtx/noop.h>
18#include <test/jtx/offer.h>
19#include <test/jtx/paths.h>
20#include <test/jtx/pay.h>
21#include <test/jtx/rate.h>
22#include <test/jtx/regkey.h>
23#include <test/jtx/sendmax.h>
24#include <test/jtx/sig.h>
25#include <test/jtx/ter.h>
26#include <test/jtx/trust.h>
27#include <test/jtx/txflags.h>
28#include <test/jtx/vault.h>
29
30#include <xrpl/basics/Number.h>
31#include <xrpl/basics/Slice.h>
32#include <xrpl/basics/base_uint.h>
33#include <xrpl/basics/strHex.h>
34#include <xrpl/beast/unit_test/suite.h>
35#include <xrpl/json/json_value.h>
36#include <xrpl/json/to_string.h>
37#include <xrpl/ledger/Dir.h>
38#include <xrpl/ledger/helpers/DelegateHelpers.h>
39#include <xrpl/protocol/Feature.h>
40#include <xrpl/protocol/Indexes.h>
41#include <xrpl/protocol/Issue.h>
42#include <xrpl/protocol/KeyType.h>
43#include <xrpl/protocol/Permissions.h>
44#include <xrpl/protocol/SField.h>
45#include <xrpl/protocol/STObject.h>
46#include <xrpl/protocol/STTx.h>
47#include <xrpl/protocol/SecretKey.h>
48#include <xrpl/protocol/TER.h>
49#include <xrpl/protocol/TxFlags.h>
50#include <xrpl/protocol/XRPAmount.h>
51#include <xrpl/protocol/jss.h>
52
53#include <algorithm>
54#include <cstddef>
55#include <cstdint>
56#include <limits>
57#include <memory>
58#include <string>
59#include <tuple>
60#include <unordered_map>
61#include <unordered_set>
62#include <vector>
63
64namespace xrpl::test {
66{
67 void
69 {
70 testcase("test feature not enabled");
71 using namespace jtx;
72
73 Env env{*this, features};
74 Account const gw{"gateway"};
75 Account const alice{"alice"};
76 Account const bob{"bob"};
77 env.fund(XRP(1000000), gw, alice, bob);
78 env.close();
79
80 auto res = features[featurePermissionDelegationV1_1] ? Ter(tesSUCCESS) : Ter(temDISABLED);
81
82 // can not set Delegate when feature disabled
83 env(delegate::set(gw, alice, {"Payment"}), res);
84 env.close();
85
86 // can not send delegating transaction when feature disabled
87 env(pay(gw, bob, XRP(100)), delegate::As(alice), res);
88 }
89
90 void
92 {
93 testcase("test valid request creating, updating, deleting permissions");
94 using namespace jtx;
95
96 Env env(*this);
97 Account const gw{"gateway"};
98 Account const alice{"alice"};
99 env.fund(XRP(100000), gw, alice);
100 env.close();
101
102 // delegating an empty permission list when the delegate ledger object
103 // does not exist is not allowed
104 env(delegate::set(gw, alice, {}), Ter(tecNO_ENTRY));
105 env.close();
106
107 auto const permissions = std::vector<std::string>{
108 "Payment", "EscrowCreate", "EscrowFinish", "TrustlineAuthorize", "CheckCreate"};
109 env(delegate::set(gw, alice, permissions));
110 env.close();
111
112 // this lambda function is used to compare the json value of ledger
113 // entry response with the given vector of permissions.
114 auto comparePermissions = [&](json::Value const& jle,
115 std::vector<std::string> const& permissions,
116 Account const& account,
117 Account const& authorize) {
118 BEAST_EXPECT(
119 !jle[jss::result].isMember(jss::error) && jle[jss::result].isMember(jss::node));
120 BEAST_EXPECT(jle[jss::result][jss::node]["LedgerEntryType"] == jss::Delegate);
121 BEAST_EXPECT(jle[jss::result][jss::node][jss::Account] == account.human());
122 BEAST_EXPECT(jle[jss::result][jss::node][sfAuthorize.jsonName] == authorize.human());
123
124 auto const& jPermissions = jle[jss::result][jss::node][sfPermissions.jsonName];
125 unsigned i = 0;
126 for (auto const& permission : permissions)
127 {
128 BEAST_EXPECT(
129 jPermissions[i][sfPermission.jsonName][sfPermissionValue.jsonName] ==
130 permission);
131 i++;
132 }
133 };
134
135 // get ledger entry with valid parameter
136 comparePermissions(delegate::entry(env, gw, alice), permissions, gw, alice);
137
138 // gw updates permission
139 auto const newPermissions =
140 std::vector<std::string>{"Payment", "AMMCreate", "AMMDeposit", "AMMWithdraw"};
141 env(delegate::set(gw, alice, newPermissions));
142 env.close();
143
144 // get ledger entry again, permissions should be updated to
145 // newPermissions
146 comparePermissions(delegate::entry(env, gw, alice), newPermissions, gw, alice);
147
148 // gw deletes all permissions delegated to alice, this will delete the ledger entry
149 env(delegate::set(gw, alice, {}));
150 env.close();
151 auto const jle = delegate::entry(env, gw, alice);
152 BEAST_EXPECT(jle[jss::result][jss::error] == "entryNotFound");
153
154 // alice can delegate permissions to gw as well
155 env(delegate::set(alice, gw, permissions));
156 env.close();
157 comparePermissions(delegate::entry(env, alice, gw), permissions, alice, gw);
158 auto const response = delegate::entry(env, gw, alice);
159 // alice has not been granted any permissions by gw
160 BEAST_EXPECT(response[jss::result][jss::error] == "entryNotFound");
161 }
162
163 void
165 {
166 testcase("test invalid DelegateSet");
167 using namespace jtx;
168
169 Env env(*this, features);
170 Account const gw{"gateway"};
171 Account const alice{"alice"};
172 Account const bob{"bob"};
173 env.fund(XRP(100000), gw, alice, bob);
174 env.close();
175
176 // when permissions size exceeds the limit 10, should return
177 // temARRAY_TOO_LARGE
178 {
179 env(delegate::set(
180 gw,
181 alice,
182 {"Payment",
183 "EscrowCreate",
184 "EscrowFinish",
185 "EscrowCancel",
186 "CheckCreate",
187 "CheckCash",
188 "CheckCancel",
189 "DepositPreauth",
190 "TrustSet",
191 "NFTokenMint",
192 "NFTokenBurn"}),
194 }
195
196 // alice can not authorize herself
197 {
198 env(delegate::set(alice, alice, {"Payment"}), Ter(temMALFORMED));
199 }
200
201 // bad fee
202 {
203 json::Value jv;
204 jv[jss::TransactionType] = jss::DelegateSet;
205 jv[jss::Account] = gw.human();
206 jv[sfAuthorize.jsonName] = alice.human();
207 json::Value permissionsJson(json::ValueType::Array);
208 json::Value permissionValue;
209 permissionValue[sfPermissionValue.jsonName] = "Payment";
210 json::Value permissionObj;
211 permissionObj[sfPermission.jsonName] = permissionValue;
212 permissionsJson.append(permissionObj);
213 jv[sfPermissions.jsonName] = permissionsJson;
214 jv[sfFee.jsonName] = -1;
215 env(jv, Ter(temBAD_FEE));
216 }
217
218 // when provided permissions contains duplicate values, should return
219 // temMALFORMED
220 {
221 env(delegate::set(
222 gw,
223 alice,
224 {"Payment",
225 "EscrowCreate",
226 "EscrowFinish",
227 "TrustlineAuthorize",
228 "CheckCreate",
229 "TrustlineAuthorize"}),
231 }
232
233 // when authorizing account which does not exist, should return
234 // tecNO_TARGET
235 {
236 env(delegate::set(gw, Account("unknown"), {"Payment"}), Ter(tecNO_TARGET));
237 }
238
239 // Delegating to a pseudo-account is not allowed, should return tecNO_PERMISSION
240 {
241 Vault const vault{env};
242 auto [tx, keylet] = vault.create({.owner = gw, .asset = xrpIssue()});
243 env(tx);
244 env.close();
245
246 auto const sleVault = env.le(keylet);
247 BEAST_EXPECT(sleVault);
248 Account const vaultPseudo{"vault", sleVault->at(sfAccount)};
249 env(delegate::set(gw, vaultPseudo, {"Payment"}), Ter(tecNO_PERMISSION));
250 }
251
252 // non-delegable transaction
253 {
254 env(delegate::set(gw, alice, {"SetRegularKey"}), Ter(temMALFORMED));
255 env(delegate::set(gw, alice, {"AccountSet"}), Ter(temMALFORMED));
256 env(delegate::set(gw, alice, {"SignerListSet"}), Ter(temMALFORMED));
257 env(delegate::set(gw, alice, {"DelegateSet"}), Ter(temMALFORMED));
258 env(delegate::set(gw, alice, {"EnableAmendment"}), Ter(temMALFORMED));
259 env(delegate::set(gw, alice, {"UNLModify"}), Ter(temMALFORMED));
260 env(delegate::set(gw, alice, {"SetFee"}), Ter(temMALFORMED));
261 env(delegate::set(gw, alice, {"Batch"}), Ter(temMALFORMED));
262 }
263 }
264
265 void
267 {
268 testcase("test reserve");
269 using namespace jtx;
270
271 // reserve requirement not met
272 {
273 Env env(*this);
274 Account const alice{"alice"};
275 Account const bob{"bob"};
276
277 auto const txFee = env.current()->fees().base;
278 env.fund(env.current()->fees().accountReserve(0) + txFee, alice);
279 env.fund(XRP(100000), bob);
280 env.close();
281
282 // alice does not have enough reserve to create Delegate
283 env(delegate::set(alice, bob, {"Payment"}), Ter(tecINSUFFICIENT_RESERVE));
284 }
285
286 // reserve recovered after deleting delegation object
287 {
288 Env env(*this);
289 Account const bob{"bob"};
290 Account const alice{"alice"};
291 Account const carol{"carol"};
292
293 auto const txFee = env.current()->fees().base;
294
295 env.fund(env.current()->fees().accountReserve(1) + (txFee * 4), alice);
296 env.fund(XRP(100000), bob, carol);
297 env.close();
298
299 // alice consumes 1 txFee and requires 1 object reserve
300 env(delegate::set(alice, bob, {"Payment"}));
301 env.close();
302
303 // alice does not have enough reserve to create another delegation object
304 env(delegate::set(alice, carol, {"Payment"}), Ter(tecINSUFFICIENT_RESERVE));
305 env.close();
306
307 // deleting delegation object recovers 1 reserve
308 env(delegate::set(alice, bob, {}));
309 env.close();
310
311 // now alice can delegate again
312 env(delegate::set(alice, carol, {"Payment"}));
313 }
314
315 // test reserve when sending transaction on behalf of other account
316 {
317 Env env(*this);
318 Account const alice{"alice"};
319 Account const bob{"bob"};
320
321 env.fund(drops(env.current()->fees().accountReserve(1)), alice);
322 env.fund(drops(env.current()->fees().accountReserve(2)), bob);
323 env.close();
324
325 // alice gives bob permission
326 env(delegate::set(alice, bob, {"DIDSet", "DIDDelete"}));
327 env.close();
328
329 // bob set DID on behalf of alice, but alice does not have enough
330 // reserve
331 env(did::set(alice), did::Uri("uri"), delegate::As(bob), Ter(tecINSUFFICIENT_RESERVE));
332
333 // bob can set DID for himself because he has enough reserve
334 env(did::set(bob), did::Uri("uri"));
335 env.close();
336 }
337 }
338
339 void
341 {
342 testcase("test fee");
343 using namespace jtx;
344
345 // Common setup: fund alice, bob, carol with 1000 XRP.
346 auto setup = [&](Env& env) {
347 Account const alice{"alice"};
348 Account const bob{"bob"};
349 Account const carol{"carol"};
350 env.fund(XRP(1000), alice, bob, carol);
351 env.close();
352 return std::make_tuple(alice, bob, carol);
353 };
354
355 // No fee deduction for terNO_DELEGATE_PERMISSION.
356 {
357 Env env(*this);
358 auto [alice, bob, carol] = setup(env);
359
360 auto const aliceBalance = env.balance(alice);
361 auto const bobBalance = env.balance(bob);
362 auto const carolBalance = env.balance(carol);
363
364 env(pay(alice, carol, XRP(100)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
365 env.close();
366 BEAST_EXPECT(env.balance(alice) == aliceBalance);
367 BEAST_EXPECT(env.balance(bob) == bobBalance);
368 BEAST_EXPECT(env.balance(carol) == carolBalance);
369 }
370
371 // Delegate pays the fee successfully.
372 {
373 Env env(*this);
374 auto [alice, bob, carol] = setup(env);
375 env(delegate::set(alice, bob, {"Payment"}));
376 env.close();
377
378 auto const aliceBalance = env.balance(alice);
379 auto const bobBalance = env.balance(bob);
380 auto const carolBalance = env.balance(carol);
381
382 auto const sendAmt = XRP(100);
383 auto const feeAmt = XRP(10);
384 env(pay(alice, carol, sendAmt), Fee(feeAmt), delegate::As(bob));
385 env.close();
386 BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt);
387 BEAST_EXPECT(env.balance(bob) == bobBalance - feeAmt);
388 BEAST_EXPECT(env.balance(carol) == carolBalance + sendAmt);
389 }
390
391 // Bob has insufficient balance to pay the fee, will get terINSUF_FEE_B.
392 {
393 Env env(*this);
394 auto [alice, bob, carol] = setup(env);
395 env(delegate::set(alice, bob, {"Payment"}));
396 env.close();
397
398 auto const aliceBalance = env.balance(alice);
399 auto const bobBalance = env.balance(bob);
400 auto const carolBalance = env.balance(carol);
401
402 env(pay(alice, carol, XRP(100)),
403 Fee(XRP(2000)),
404 delegate::As(bob),
406 env.close();
407 BEAST_EXPECT(env.balance(alice) == aliceBalance);
408 BEAST_EXPECT(env.balance(bob) == bobBalance);
409 BEAST_EXPECT(env.balance(carol) == carolBalance);
410 }
411
412 // The delegated account has enough balance to pay and delegator has enough reserve
413 {
414 // Common setup: fund accounts and grant Bob permission to pay on Alice's behalf.
415 // Alice is funded with exactly (paymentAmount + reserve + baseFee): baseFee covers
416 // the DelegateSet tx cost, leaving Alice with exactly (paymentAmount + reserve).
417 // highFee = reserve + baseFee, strictly greater than reserve, so that
418 // max(reserve, highFee) = highFee — making the direct payment check fail.
419 auto setup = [&](Env& env) {
420 Account const alice{"alice"};
421 Account const bob{"bob"};
422 Account const carol{"carol"};
423
424 auto const baseFee = env.current()->fees().base;
425 auto const reserve = env.current()->fees().accountReserve(1);
426 auto const paymentAmount = XRP(1);
427 auto const highFee = reserve + baseFee;
428 BEAST_EXPECT(highFee > reserve);
429
430 env.fund(paymentAmount + reserve + baseFee, alice);
431 env.fund(XRP(1000), bob);
432 env.fund(XRP(1000), carol);
433 env.close();
434
435 env(delegate::set(alice, bob, {"Payment"}));
436 env.close();
437
438 env.require(Balance(alice, paymentAmount + reserve));
439
440 return std::make_tuple(alice, bob, carol, paymentAmount, highFee, reserve);
441 };
442
443 // Alice's balance (paymentAmount + reserve) is insufficient to cover both
444 // the payment and highFee directly. Even though fees are allowed to dip
445 // below reserve, when Alice pays the fee herself the required funds =
446 // paymentAmount + max(reserve, highFee) = paymentAmount + highFee
447 // (since highFee > reserve), which still exceeds her balance.
448 // tec: highFee is consumed from Alice's balance.
449 {
450 Env env(*this);
451 auto [alice, bob, carol, paymentAmount, highFee, reserve] = setup(env);
452 auto const aliceBalance = env.balance(alice);
453 auto const bobBalance = env.balance(bob);
454 auto const carolBalance = env.balance(carol);
455
456 env(pay(alice, carol, paymentAmount), Fee(highFee), Ter(tecUNFUNDED_PAYMENT));
457
458 // tec consumes the fee from Alice; carol and bob are unaffected.
459 BEAST_EXPECT(env.balance(alice) == aliceBalance - highFee);
460 BEAST_EXPECT(env.balance(bob) == bobBalance);
461 BEAST_EXPECT(env.balance(carol) == carolBalance);
462 }
463
464 // The payment succeeds because the delegated account pays the fee.
465 // Alice only needs (paymentAmount + reserve).
466 {
467 Env env(*this);
468 auto [alice, bob, carol, paymentAmount, highFee, reserve] = setup(env);
469
470 auto const alicePrePay = env.balance(alice, XRP);
471 auto const bobPrePay = env.balance(bob, XRP);
472 auto const carolPrePay = env.balance(carol, XRP);
473
474 env(pay(alice, carol, paymentAmount), delegate::As(bob), Fee(highFee));
475 env.close();
476
477 env.require(Balance(alice, alicePrePay - paymentAmount));
478 env.require(Balance(bob, bobPrePay - highFee));
479 env.require(Balance(carol, carolPrePay + paymentAmount));
480 }
481 }
482
483 // Delegated account can pay the fee even if it dips below reserve.
484 {
485 Env env(*this);
486 Account const alice{"alice"};
487 Account const bob{"bob"};
488 Account const carol{"carol"};
489
490 auto const baseFee = env.current()->fees().base;
491 auto const baseReserve = env.current()->fees().accountReserve(0);
492
493 env.fund(env.current()->fees().accountReserve(1) + baseFee + XRP(1), alice);
494 env.fund(baseReserve, bob);
495 env.fund(XRP(1000), carol);
496 env.close();
497
498 env(delegate::set(alice, bob, {"Payment"}));
499 env.close();
500
501 auto const alicePreTx = env.balance(alice, XRP);
502 auto const bobPreTx = env.balance(bob, XRP);
503
504 // After paying for this transaction, bob's balance will
505 // dip below the base reserve
506 env(pay(alice, carol, XRP(1)), delegate::As(bob));
507 env.close();
508
509 // Bob's balance is now less than the base reserve.
510 BEAST_EXPECT(env.balance(bob, XRP) < baseReserve);
511 env.require(Balance(bob, bobPreTx - drops(baseFee)));
512
513 // Alice's balance only decreased by the 1.0 XRP she sent.
514 env.require(Balance(alice, alicePreTx - XRP(1)));
515 }
516
517 // The delegated account has enough balance for the fee, but delegator
518 // runs into tecUNFUNDED_PAYMENT.
519 {
520 Env env(*this);
521 Account const alice{"alice"};
522 Account const bob{"bob"};
523 Account const carol{"carol"};
524
525 auto const baseFee = env.current()->fees().base;
526 auto const reserve = env.current()->fees().accountReserve(1);
527
528 // Alice is funded with (reserve + baseFee): after DelegateSet she has
529 // exactly 'reserve', which is insufficient to send XRP(10) while keeping
530 // reserve. Bob has plenty to pay the fee.
531 env.fund(reserve + baseFee, alice);
532 env.fund(XRP(1000), bob);
533 env.fund(XRP(1000), carol);
534 env.close();
535
536 env(delegate::set(alice, bob, {"Payment"}));
537 env.close();
538
539 auto const alicePrePay = env.balance(alice, XRP);
540 auto const bobPrePay = env.balance(bob, XRP);
541 auto const carolPrePay = env.balance(carol, XRP);
542
543 // Bob pays the fee, but Alice has insufficient balance to send XRP(10).
544 env(pay(alice, carol, XRP(10)), delegate::As(bob), Ter(tecUNFUNDED_PAYMENT));
545
546 env.require(Balance(alice, alicePrePay));
547 env.require(Balance(bob, bobPrePay - drops(baseFee)));
548 env.require(Balance(carol, carolPrePay));
549 }
550 }
551
552 void
554 {
555 testcase("test sequence");
556 using namespace jtx;
557
558 Env env(*this);
559 Account const alice{"alice"};
560 Account const bob{"bob"};
561 Account const carol{"carol"};
562 env.fund(XRP(10000), alice, bob, carol);
563 env.close();
564
565 auto aliceSeq = env.seq(alice);
566 auto bobSeq = env.seq(bob);
567 env(delegate::set(alice, bob, {"Payment"}));
568 env(delegate::set(bob, alice, {"Payment"}));
569 env.close();
570 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
571 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
572 aliceSeq = env.seq(alice);
573 bobSeq = env.seq(bob);
574
575 for (auto i = 0; i < 20; ++i)
576 {
577 // bob is the delegated account, his sequence won't kIncrement
578 env(pay(alice, carol, XRP(10)), Fee(XRP(10)), delegate::As(bob));
579 env.close();
580 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
581 BEAST_EXPECT(env.seq(bob) == bobSeq);
582 aliceSeq = env.seq(alice);
583
584 // bob sends payment for himself, his sequence will kIncrement
585 env(pay(bob, carol, XRP(10)), Fee(XRP(10)));
586 BEAST_EXPECT(env.seq(alice) == aliceSeq);
587 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
588 bobSeq = env.seq(bob);
589
590 // alice is the delegated account, her sequence won't kIncrement
591 env(pay(bob, carol, XRP(10)), Fee(XRP(10)), delegate::As(alice));
592 env.close();
593 BEAST_EXPECT(env.seq(alice) == aliceSeq);
594 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
595 bobSeq = env.seq(bob);
596
597 // alice sends payment for herself, her sequence will kIncrement
598 env(pay(alice, carol, XRP(10)), Fee(XRP(10)));
599 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
600 BEAST_EXPECT(env.seq(bob) == bobSeq);
601 aliceSeq = env.seq(alice);
602 }
603 }
604
605 void
607 {
608 testcase("test deleting account");
609 using namespace jtx;
610
611 // Delegator (alice) deletes account: Delegate object is cleaned up from
612 // both alice's and bob's owner directories.
613 {
614 Env env(*this);
615 Account const alice{"alice"};
616 Account const bob{"bob"};
617 Account const carol{"carol"};
618 env.fund(XRP(100000), alice, bob, carol);
619 env.close();
620
621 env(delegate::set(alice, bob, {"Payment"}));
622 env.close();
623
624 auto const delegateKey = keylet::delegate(alice.id(), bob.id());
625 BEAST_EXPECT(env.closed()->exists(delegateKey));
626
627 auto hasKey = [](xrpl::Dir const& dir, uint256 const& key) {
628 return std::any_of( // NOLINT(modernize-use-ranges)
629 dir.begin(), dir.end(), [&](auto const& sle) { return sle->key() == key; });
630 };
631
632 // Delegate object should appear in both alice's and bob's directories
633 BEAST_EXPECT(
634 hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(alice.id())), delegateKey.key));
635 BEAST_EXPECT(
636 hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(bob.id())), delegateKey.key));
637
638 for (std::uint32_t i = 0; i < 256; ++i)
639 env.close();
640
641 auto const aliceBalance = env.balance(alice);
642 auto const carolBalance = env.balance(carol);
643
644 // alice deletes account, this will remove the Delegate object from
645 // both alice's and bob's owner directories
646 auto const deleteFee = drops(env.current()->fees().increment);
647 env(acctdelete(alice, carol), Fee(deleteFee));
648 env.close();
649
650 BEAST_EXPECT(!env.closed()->exists(keylet::account(alice.id())));
651 BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(alice.id())));
652 BEAST_EXPECT(!env.closed()->exists(delegateKey));
653 // bob's directory should no longer reference the Delegate object
654 BEAST_EXPECT(
655 !hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(bob.id())), delegateKey.key));
656 BEAST_EXPECT(env.balance(carol) == carolBalance + aliceBalance - deleteFee);
657 }
658
659 // Delegatee (bob) deletes account: Delegate object is cleaned up from
660 // both alice's and bob's owner directories, freeing alice's reserve so
661 // she can subsequently delete her own account.
662 {
663 Env env(*this);
664 Account const alice{"alice"};
665 Account const bob{"bob"};
666 Account const carol{"carol"};
667 env.fund(XRP(100000), alice, bob, carol);
668 env.close();
669
670 env(delegate::set(alice, bob, {"Payment"}));
671 env.close();
672
673 auto const delegateKey = keylet::delegate(alice.id(), bob.id());
674 BEAST_EXPECT(env.closed()->exists(delegateKey));
675
676 auto hasKey = [](xrpl::Dir const& dir, uint256 const& key) {
677 return std::any_of( // NOLINT(modernize-use-ranges)
678 dir.begin(), dir.end(), [&](auto const& sle) { return sle->key() == key; });
679 };
680
681 BEAST_EXPECT(
682 hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(alice.id())), delegateKey.key));
683 BEAST_EXPECT(
684 hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(bob.id())), delegateKey.key));
685
686 // The Delegate entry counts against alice's ownerCount.
687 auto const sleAlice = env.closed()->read(keylet::account(alice.id()));
688 BEAST_EXPECT(sleAlice);
689 BEAST_EXPECT(sleAlice->getFieldU32(sfOwnerCount) == 1);
690
691 for (std::uint32_t i = 0; i < 256; ++i)
692 env.close();
693
694 auto const bobBalance = env.balance(bob);
695 auto const carolBalance = env.balance(carol);
696
697 // bob (the authorized/delegatee account) deletes his account.
698 // This must clean up the Delegate object from both alice's and
699 // bob's owner directories so alice's delegation does not survive
700 // a potential account resurrection.
701 auto const deleteFee = drops(env.current()->fees().increment);
702 env(acctdelete(bob, carol), Fee(deleteFee));
703 env.close();
704
705 BEAST_EXPECT(!env.closed()->exists(keylet::account(bob.id())));
706 BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(bob.id())));
707 BEAST_EXPECT(!env.closed()->exists(delegateKey));
708 // alice's directory should no longer reference the Delegate object
709 BEAST_EXPECT(
710 !hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(alice.id())), delegateKey.key));
711 BEAST_EXPECT(env.balance(carol) == carolBalance + bobBalance - deleteFee);
712
713 // alice's ownerCount is now 0; she can delete her own account.
714 auto const sleAlice2 = env.closed()->read(keylet::account(alice.id()));
715 BEAST_EXPECT(sleAlice2);
716 BEAST_EXPECT(sleAlice2->getFieldU32(sfOwnerCount) == 0);
717
718 auto const aliceDeleteFee = drops(env.current()->fees().increment);
719 env(acctdelete(alice, carol), Fee(aliceDeleteFee));
720 env.close();
721
722 BEAST_EXPECT(!env.closed()->exists(keylet::account(alice.id())));
723 }
724
725 // Multiple delegators -> same delegatee: when the delegatee (bob)
726 // deletes his account, ALL Delegate objects (from alice and carol)
727 // must be cleaned up from every delegator's directory.
728 {
729 Env env(*this);
730 Account const alice{"alice"};
731 Account const bob{"bob"};
732 Account const carol{"carol"};
733 Account const dave{"dave"};
734 env.fund(XRP(100000), alice, bob, carol, dave);
735 env.close();
736
737 // Both alice and carol delegate to bob
738 env(delegate::set(alice, bob, {"Payment"}));
739 env(delegate::set(carol, bob, {"EscrowCreate"}));
740 env.close();
741
742 auto const aliceBobKey = keylet::delegate(alice.id(), bob.id());
743 auto const carolBobKey = keylet::delegate(carol.id(), bob.id());
744
745 auto hasKey = [](xrpl::Dir const& dir, uint256 const& key) {
746 return std::any_of( // NOLINT(modernize-use-ranges)
747 dir.begin(), dir.end(), [&](auto const& sle) { return sle->key() == key; });
748 };
749
750 // Both Delegate objects exist and are in bob's directory
751 BEAST_EXPECT(env.closed()->exists(aliceBobKey));
752 BEAST_EXPECT(env.closed()->exists(carolBobKey));
753 BEAST_EXPECT(
754 hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(bob.id())), aliceBobKey.key));
755 BEAST_EXPECT(
756 hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(bob.id())), carolBobKey.key));
757
758 for (std::uint32_t i = 0; i < 256; ++i)
759 env.close();
760
761 auto const bobBalance = env.balance(bob);
762 auto const daveBalance = env.balance(dave);
763
764 auto const deleteFee = drops(env.current()->fees().increment);
765 env(acctdelete(bob, dave), Fee(deleteFee));
766 env.close();
767
768 // bob's account and directory are gone
769 BEAST_EXPECT(!env.closed()->exists(keylet::account(bob.id())));
770 BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(bob.id())));
771
772 // Both Delegate objects are erased
773 BEAST_EXPECT(!env.closed()->exists(aliceBobKey));
774 BEAST_EXPECT(!env.closed()->exists(carolBobKey));
775
776 // alice's and carol's directories no longer reference the objects
777 BEAST_EXPECT(
778 !hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(alice.id())), aliceBobKey.key));
779 BEAST_EXPECT(
780 !hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(carol.id())), carolBobKey.key));
781
782 BEAST_EXPECT(env.balance(dave) == daveBalance + bobBalance - deleteFee);
783 }
784 }
785
786 void
788 {
789 testcase("test delegate transaction");
790 using namespace jtx;
791
792 Env env(*this);
793 Account const alice{"alice"};
794 Account const bob{"bob"};
795 Account const carol{"carol"};
796
797 XRPAmount const baseFee{env.current()->fees().base};
798
799 // use different initial amount to distinguish the source balance
800 env.fund(XRP(10000), alice);
801 env.fund(XRP(20000), bob);
802 env.fund(XRP(30000), carol);
803 env.close();
804
805 auto aliceBalance = env.balance(alice, XRP);
806 auto bobBalance = env.balance(bob, XRP);
807 auto carolBalance = env.balance(carol, XRP);
808
809 // can not send transaction on one's own behalf
810 env(pay(alice, bob, XRP(50)), delegate::As(alice), Ter(temBAD_SIGNER));
811 env.require(Balance(alice, aliceBalance));
812
813 env(delegate::set(alice, bob, {"Payment"}));
814 env.close();
815 env.require(Balance(alice, aliceBalance - drops(baseFee)));
816 aliceBalance = env.balance(alice, XRP);
817
818 // bob pays 50 XRP to carol on behalf of alice
819 env(pay(alice, carol, XRP(50)), delegate::As(bob));
820 env.close();
821 env.require(Balance(alice, aliceBalance - XRP(50)));
822 env.require(Balance(carol, carolBalance + XRP(50)));
823 // bob pays the fee
824 env.require(Balance(bob, bobBalance - drops(baseFee)));
825 aliceBalance = env.balance(alice, XRP);
826 bobBalance = env.balance(bob, XRP);
827 carolBalance = env.balance(carol, XRP);
828
829 // bob pays 50 XRP to bob self on behalf of alice
830 env(pay(alice, bob, XRP(50)), delegate::As(bob));
831 env.close();
832 env.require(Balance(alice, aliceBalance - XRP(50)));
833 env.require(Balance(bob, bobBalance + XRP(50) - drops(baseFee)));
834 aliceBalance = env.balance(alice, XRP);
835 bobBalance = env.balance(bob, XRP);
836
837 // bob pay 50 XRP to alice herself on behalf of alice
838 env(pay(alice, alice, XRP(50)), delegate::As(bob), Ter(temREDUNDANT));
839 env.close();
840
841 // bob does not have permission to create check
842 env(check::create(alice, bob, XRP(10)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
843
844 // carol does not have permission to create check
845 env(check::create(alice, bob, XRP(10)),
846 delegate::As(carol),
848 }
849
850 void
852 {
853 testcase("test payment granular");
854 using namespace jtx;
855
856 // test PaymentMint and PaymentBurn
857 {
858 Env env(*this);
859 Account const alice{"alice"};
860 Account const bob{"bob"};
861 Account const gw{"gateway"};
862 Account const gw2{"gateway2"};
863 auto const usd = gw["USD"];
864 auto const eur = gw2["EUR"];
865
866 env.fund(XRP(10000), alice);
867 env.fund(XRP(20000), bob);
868 env.fund(XRP(40000), gw, gw2);
869 env.trust(usd(200), alice);
870 env.trust(eur(400), gw);
871 env.close();
872
873 XRPAmount const baseFee{env.current()->fees().base};
874 auto aliceBalance = env.balance(alice, XRP);
875 auto bobBalance = env.balance(bob, XRP);
876 auto gwBalance = env.balance(gw, XRP);
877 auto gw2Balance = env.balance(gw2, XRP);
878
879 // delegate ledger object is not created yet
880 env(pay(gw, alice, usd(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
881 env.require(Balance(bob, bobBalance));
882
883 // gw gives bob burn permission
884 env(delegate::set(gw, bob, {"PaymentBurn"}));
885 env.close();
886 env.require(Balance(gw, gwBalance - drops(baseFee)));
887 gwBalance = env.balance(gw, XRP);
888
889 // bob sends a payment transaction on behalf of gw
890 env(pay(gw, alice, usd(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
891 env.close();
892 env.require(Balance(bob, bobBalance));
893
894 // gw gives bob mint permission, alice gives bob burn permission
895 env(delegate::set(gw, bob, {"PaymentMint"}));
896 env(delegate::set(alice, bob, {"PaymentBurn"}));
897 env.close();
898 env.require(Balance(alice, aliceBalance - drops(baseFee)));
899 env.require(Balance(gw, gwBalance - drops(baseFee)));
900 aliceBalance = env.balance(alice, XRP);
901 gwBalance = env.balance(gw, XRP);
902
903 // can not send XRP
904 env(pay(gw, alice, XRP(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
905 env.close();
906 env.require(Balance(bob, bobBalance));
907
908 // mint 50 USD
909 env(pay(gw, alice, usd(50)), delegate::As(bob));
910 env.close();
911 env.require(Balance(bob, bobBalance - drops(baseFee)));
912 env.require(Balance(gw, gwBalance));
913 env.require(Balance(gw, alice["USD"](-50)));
914 env.require(Balance(alice, usd(50)));
915 BEAST_EXPECT(env.balance(bob, usd) == usd(0));
916 bobBalance = env.balance(bob, XRP);
917
918 // burn 30 USD
919 env(pay(alice, gw, usd(30)), delegate::As(bob));
920 env.close();
921 env.require(Balance(bob, bobBalance - drops(baseFee)));
922 env.require(Balance(gw, gwBalance));
923 env.require(Balance(gw, alice["USD"](-20)));
924 env.require(Balance(alice, usd(20)));
925 BEAST_EXPECT(env.balance(bob, usd) == usd(0));
926 bobBalance = env.balance(bob, XRP);
927
928 // bob has both mint and burn permissions
929 env(delegate::set(gw, bob, {"PaymentMint", "PaymentBurn"}));
930 env.close();
931 env.require(Balance(gw, gwBalance - drops(baseFee)));
932 gwBalance = env.balance(gw, XRP);
933
934 // mint 100 USD for gw
935 env(pay(gw, alice, usd(100)), delegate::As(bob));
936 env.close();
937 env.require(Balance(gw, alice["USD"](-120)));
938 env.require(Balance(alice, usd(120)));
939 env.require(Balance(bob, bobBalance - drops(baseFee)));
940 bobBalance = env.balance(bob, XRP);
941
942 // gw2 pays gw 200 EUR
943 env(pay(gw2, gw, eur(200)));
944 env.close();
945 env.require(Balance(gw2, gw2Balance - drops(baseFee)));
946 gw2Balance = env.balance(gw2, XRP);
947 env.require(Balance(gw2, gw["EUR"](-200)));
948 env.require(Balance(gw, eur(200)));
949
950 // burn 100 EUR for gw
951 env(pay(gw, gw2, eur(100)), delegate::As(bob));
952 env.close();
953 env.require(Balance(gw2, gw["EUR"](-100)));
954 env.require(Balance(gw, eur(100)));
955 env.require(Balance(bob, bobBalance - drops(baseFee)));
956 env.require(Balance(gw, gwBalance));
957 env.require(Balance(gw2, gw2Balance));
958 env.require(Balance(alice, aliceBalance));
959 }
960
961 // test PaymentMint won't affect Payment transaction level delegation.
962 {
963 Env env(*this);
964 Account const alice{"alice"};
965 Account const bob{"bob"};
966 Account const gw{"gateway"};
967 auto const usd = gw["USD"];
968
969 env.fund(XRP(10000), alice);
970 env.fund(XRP(20000), bob);
971 env.fund(XRP(40000), gw);
972 env.trust(usd(200), alice);
973 env.close();
974
975 XRPAmount const baseFee{env.current()->fees().base};
976
977 auto aliceBalance = env.balance(alice, XRP);
978 auto bobBalance = env.balance(bob, XRP);
979 auto gwBalance = env.balance(gw, XRP);
980
981 // gw gives bob PaymentBurn permission
982 env(delegate::set(gw, bob, {"PaymentBurn"}));
983 env.close();
984 env.require(Balance(gw, gwBalance - drops(baseFee)));
985 gwBalance = env.balance(gw, XRP);
986
987 // bob can not mint on behalf of gw because he only has burn
988 // permission
989 env(pay(gw, alice, usd(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
990 env.close();
991 env.require(Balance(bob, bobBalance));
992
993 // gw gives bob Payment permission as well
994 env(delegate::set(gw, bob, {"PaymentBurn", "Payment"}));
995 env.close();
996 env.require(Balance(gw, gwBalance - drops(baseFee)));
997 gwBalance = env.balance(gw, XRP);
998
999 // bob now can mint on behalf of gw
1000 env(pay(gw, alice, usd(50)), delegate::As(bob));
1001 env.close();
1002 env.require(Balance(bob, bobBalance - drops(baseFee)));
1003 env.require(Balance(gw, gwBalance));
1004 env.require(Balance(alice, aliceBalance));
1005 env.require(Balance(gw, alice["USD"](-50)));
1006 env.require(Balance(alice, usd(50)));
1007 BEAST_EXPECT(env.balance(bob, usd) == usd(0));
1008 }
1009
1010 // disallow cross currency payment with only PaymentBurn/PaymentMint
1011 // permission
1012 {
1013 Env env(*this, features);
1014 Account const alice{"alice"};
1015 Account const bob{"bob"};
1016 Account const gw{"gateway"};
1017 Account const carol{"carol"};
1018 auto const usd = gw["USD"];
1019
1020 env.fund(XRP(10000), alice, bob, carol, gw);
1021 env.close();
1022 env.trust(usd(50000), alice);
1023 env.trust(usd(50000), bob);
1024 env.trust(usd(50000), carol);
1025 env(pay(gw, alice, usd(10000)));
1026 env(pay(gw, bob, usd(10000)));
1027 env(pay(gw, carol, usd(10000)));
1028 env.close();
1029
1030 // PaymentMint
1031 {
1032 env(offer(carol, XRP(100), usd(501)));
1033 BEAST_EXPECT(expectOffers(env, carol, 1));
1034 env(delegate::set(gw, bob, {"PaymentMint"}));
1035 env.close();
1036
1037 // bob can not send cross currency payment on behalf of the gw,
1038 // even with PaymentMint permission and gw being the issuer.
1039 env(pay(gw, alice, usd(5000)),
1040 Sendmax(XRP(1001)),
1041 Txflags(tfPartialPayment),
1042 delegate::As(bob),
1044 BEAST_EXPECT(expectOffers(env, carol, 1));
1045
1046 env(pay(gw, alice, usd(5000)),
1047 Path(~XRP),
1048 Txflags(tfPartialPayment),
1049 delegate::As(bob),
1051 BEAST_EXPECT(expectOffers(env, carol, 1));
1052
1053 // succeed with direct payment
1054 env(pay(gw, alice, usd(100)), delegate::As(bob));
1055 env.close();
1056 }
1057
1058 // PaymentBurn
1059 {
1060 env(offer(bob, XRP(100), usd(501)));
1061 BEAST_EXPECT(expectOffers(env, bob, 1));
1062 env(delegate::set(alice, bob, {"PaymentBurn"}));
1063 env.close();
1064
1065 // bob can not send cross currency payment on behalf of alice,
1066 // even with PaymentBurn permission and gw being the issuer.
1067 env(pay(alice, gw, usd(5000)),
1068 Sendmax(XRP(1001)),
1069 Txflags(tfPartialPayment),
1070 delegate::As(bob),
1072 BEAST_EXPECT(expectOffers(env, bob, 1));
1073
1074 env(pay(alice, gw, usd(5000)),
1075 Path(~XRP),
1076 Txflags(tfPartialPayment),
1077 delegate::As(bob),
1079 BEAST_EXPECT(expectOffers(env, bob, 1));
1080
1081 // succeed with direct payment
1082 env(pay(alice, gw, usd(100)), delegate::As(bob));
1083 env.close();
1084 }
1085 }
1086
1087 // PaymentMint/PaymentBurn with sfSendMax of the same asset is allowed,
1088 // same-asset SendMax is still a direct payment, not cross-currency.
1089 {
1090 Env env(*this, features);
1091 Account const alice{"alice"};
1092 Account const bob{"bob"};
1093 Account const gw{"gw"};
1094 auto const usd = gw["USD"];
1095 env.fund(XRP(10000), alice, bob, gw);
1096 env.trust(usd(200), alice);
1097 env.close();
1098
1099 env(delegate::set(gw, bob, {"PaymentMint"}));
1100 env.close();
1101
1102 // sfSendMax with same asset as sfAmount, still a direct payment
1103 env(pay(gw, alice, usd(50)), Sendmax(usd(50)), delegate::As(bob));
1104 env.require(Balance(alice, usd(50)));
1105
1106 env(delegate::set(alice, bob, {"PaymentBurn"}));
1107 env.close();
1108
1109 env(pay(alice, gw, usd(30)), delegate::As(bob));
1110 env.require(Balance(alice, usd(20)));
1111 }
1112
1113 // PaymentBurn is authorized by balance direction, not trust limit.
1114 // holder is allowed to burn even if trust limit is 0.
1115 {
1116 Env env(*this);
1117 Account const alice{"alice"};
1118 Account const bob{"bob"};
1119 Account const gw{"gateway"};
1120 auto const gwUSD = gw["USD"];
1121 auto const aliceUSD = alice["USD"];
1122
1123 env.fund(XRP(10000), alice, bob, gw);
1124 env.trust(gwUSD(200), alice);
1125 env.close();
1126
1127 env(pay(gw, alice, gwUSD(50)));
1128 env.require(Balance(alice, gwUSD(50)));
1129 env.close();
1130
1131 env(delegate::set(alice, bob, {"PaymentBurn"}));
1132 env.close();
1133
1134 env.trust(gwUSD(0), alice);
1135 env.close();
1136 BEAST_EXPECT(env.limit(alice, gwUSD.issue()) == gwUSD(0));
1137
1138 env(trust(gw, aliceUSD(200)));
1139 env.close();
1140
1141 env(pay(alice, gw, gwUSD(30)), delegate::As(bob));
1142 env.require(Balance(alice, gwUSD(20)));
1143 env.require(Balance(gw, aliceUSD(-20)));
1144 }
1145
1146 // Test invalid fields or flags not allowed in granular permission template
1147 {
1148 Env env(*this, features);
1149 Account const alice{"alice"};
1150 Account const bob{"bob"};
1151 Account const gw{"gw"};
1152 auto const usd = gw["USD"];
1153 env.fund(XRP(10000), alice, bob, gw);
1154 env.trust(usd(200), alice);
1155 env.close();
1156
1157 env(delegate::set(gw, bob, {"PaymentMint"}));
1158 env(delegate::set(alice, bob, {"PaymentBurn"}));
1159 env.close();
1160
1161 // sfDeliverMin (with tfPartialPayment) is not in the PaymentMint
1162 // or PaymentBurn template.
1163 env(pay(gw, alice, usd(100)),
1164 DeliverMin(usd(50)),
1165 Txflags(tfPartialPayment),
1166 delegate::As(bob),
1168 env(pay(alice, gw, usd(50)),
1169 DeliverMin(usd(25)),
1170 Txflags(tfPartialPayment),
1171 delegate::As(bob),
1173
1174 // sfDomainID is not in the PaymentMint or PaymentBurn template.
1175 env(pay(gw, alice, usd(100)),
1176 Domain(uint256{1}),
1177 delegate::As(bob),
1179 env(pay(alice, gw, usd(50)),
1180 Domain(uint256{1}),
1181 delegate::As(bob),
1183 }
1184
1185 // Delegate account holds no granular permissions for the tx type:
1186 // getGranularPermission returns empty set.
1187 {
1188 Env env(*this, features);
1189 Account const alice{"alice"};
1190 Account const bob{"bob"};
1191 Account const gw{"gw"};
1192 auto const usd = gw["USD"];
1193 env.fund(XRP(10000), alice, bob, gw);
1194 env.trust(usd(200), alice);
1195 env.close();
1196
1197 // Bob holds only an AccountSet granular permission.
1198 env(delegate::set(alice, bob, {"AccountDomainSet"}));
1199 env.close();
1200
1201 // Payment has granular permissions defined in permissions.macro,
1202 // but bob only holds AccountSet's granular permission,
1203 // getGranularPermission returns empty.
1204 env(pay(alice, gw, usd(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1205 }
1206
1207 // PaymentMint and PaymentBurn for MPT
1208 {
1209 std::string logs;
1210 Env env(*this, features, std::make_unique<CaptureLogs>(&logs));
1211 Account const alice{"alice"};
1212 Account const bob{"bob"};
1213 Account const gw{"gateway"};
1214
1215 MPTTester mpt(env, gw, {.holders = {alice, bob}});
1216 mpt.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
1217
1218 mpt.authorize({.account = alice});
1219 mpt.authorize({.account = bob});
1220
1221 auto const gwMPT = mpt["MPT"];
1222 env(pay(gw, alice, gwMPT(500)));
1223 env(pay(gw, bob, gwMPT(500)));
1224 env.close();
1225 auto aliceMPT = env.balance(alice, gwMPT);
1226 auto bobMPT = env.balance(bob, gwMPT);
1227
1228 // PaymentMint
1229 {
1230 env(delegate::set(gw, bob, {"PaymentMint"}));
1231 env.close();
1232
1233 env(pay(gw, alice, gwMPT(50)), delegate::As(bob));
1234 BEAST_EXPECT(env.balance(alice, gwMPT) == aliceMPT + gwMPT(50));
1235 BEAST_EXPECT(env.balance(bob, gwMPT) == bobMPT);
1236 aliceMPT = env.balance(alice, gwMPT);
1237 }
1238
1239 // PaymentBurn
1240 {
1241 env(delegate::set(alice, bob, {"PaymentBurn"}));
1242 env.close();
1243
1244 env(pay(alice, gw, gwMPT(50)), delegate::As(bob));
1245 BEAST_EXPECT(env.balance(alice, gwMPT) == aliceMPT - gwMPT(50));
1246 BEAST_EXPECT(env.balance(bob, gwMPT) == bobMPT);
1247 aliceMPT = env.balance(alice, gwMPT);
1248 }
1249
1250 // Grant both granular permissions and tx level permission.
1251 {
1252 env(delegate::set(alice, bob, {"PaymentBurn", "PaymentMint", "Payment"}));
1253 env.close();
1254 env(pay(alice, gw, gwMPT(50)), delegate::As(bob));
1255 BEAST_EXPECT(env.balance(alice, gwMPT) == aliceMPT - gwMPT(50));
1256 BEAST_EXPECT(env.balance(bob, gwMPT) == bobMPT);
1257 aliceMPT = env.balance(alice, gwMPT);
1258 env(pay(alice, bob, gwMPT(100)), delegate::As(bob));
1259 BEAST_EXPECT(env.balance(alice, gwMPT) == aliceMPT - gwMPT(100));
1260 BEAST_EXPECT(env.balance(bob, gwMPT) == bobMPT + gwMPT(100));
1261 }
1262 }
1263
1264 // PaymentMint/PaymentBurn must not trust IOU issuer aliases.
1265 // In a direct IOU payment, sfAmount.issuer may be encoded as either
1266 // endpoint, and PaySteps normalizes those aliases to the same execution.
1267 // These cases ensure a delegate cannot flip the encoded issuer to turn a
1268 // mint into an apparent burn, or a burn into an apparent mint.
1269 {
1270 Env env(*this);
1271 Account const alice{"alice"};
1272 Account const bob{"bob"};
1273 Account const gw{"gateway"};
1274 auto const gwUSD = gw["USD"];
1275 auto const aliceUSD = alice["USD"];
1276
1277 env.fund(XRP(10000), alice, bob, gw);
1278 env.trust(gwUSD(200), alice);
1279 env.close();
1280
1281 // Alice holds 100 USD issued by gw.
1282 env(pay(gw, alice, gwUSD(100)));
1283 env.close();
1284 env.require(Balance(alice, gwUSD(100)));
1285
1286 // Delegate with only PaymentBurn tries to mint by encoding
1287 // Amount.issuer as the destination alias, alice. The actual issuer
1288 // is gw, so this requires PaymentMint and must be rejected.
1289 {
1290 env(delegate::set(gw, bob, {"PaymentBurn"}));
1291 env.close();
1292
1293 // Amount.issuer = alice (destination), rejected because gw is
1294 // the actual issuer and PaymentMint is required.
1295 env(pay(gw, alice, aliceUSD(50)),
1296 delegate::As(bob),
1298 env.require(Balance(alice, gwUSD(100)));
1299
1300 // Fails because bob holds PaymentBurn, not PaymentMint.
1301 env(pay(gw, alice, gwUSD(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1302 env.require(Balance(alice, gwUSD(100)));
1303 }
1304
1305 // Delegate with only PaymentMint tries to burn by encoding
1306 // Amount.issuer as the source alias, alice. The actual issuer is
1307 // gw, so this requires PaymentBurn and must be rejected.
1308 {
1309 env(delegate::set(alice, bob, {"PaymentMint"}));
1310 env.close();
1311
1312 // Amount.issuer = alice (account), rejected because gw is the
1313 // actual issuer and PaymentBurn is required.
1314 env(pay(alice, gw, aliceUSD(50)),
1315 delegate::As(bob),
1317 env.require(Balance(alice, gwUSD(100)));
1318
1319 // Fails because bob holds PaymentMint, not PaymentBurn.
1320 env(pay(alice, gw, gwUSD(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1321 env.require(Balance(alice, gwUSD(100)));
1322 }
1323 }
1324
1325 // Neither account nor destination is issuer.
1326 // PaymentMint and PaymentBurn do not authorize these payments.
1327 {
1328 // IOU
1329 {
1330 Env env(*this);
1331 Account const alice{"alice"};
1332 Account const bob{"bob"};
1333 Account const gw{"gateway"};
1334 Account const gw2{"gateway2"};
1335 auto const gwUSD = gw["USD"];
1336
1337 env.fund(XRP(10000), alice, bob, gw, gw2);
1338 env.close();
1339
1340 env.trust(gwUSD(200), alice);
1341 env.close();
1342
1343 env(pay(gw, alice, gwUSD(100)));
1344 env.close();
1345
1346 env(delegate::set(alice, bob, {"PaymentMint", "PaymentBurn"}));
1347 env.close();
1348
1349 env(pay(alice, gw, gw2["USD"](50)),
1350 delegate::As(bob),
1352 }
1353
1354 // MPT
1355 {
1356 Env env(*this, features);
1357 Account const alice{"alice"};
1358 Account const bob{"bob"};
1359 Account const gw{"gateway"};
1360 Account const gw2{"gateway2"};
1361
1362 env.fund(XRP(10000), gw2);
1363 env.close();
1364
1365 MPTTester mpt(env, gw, {.holders = {alice, bob}});
1366 mpt.create({.ownerCount = 1, .flags = tfMPTCanTransfer});
1367
1368 mpt.authorize({.account = alice});
1369 mpt.authorize({.account = bob});
1370
1371 auto const gwMPT = mpt["MPT"];
1372 env(pay(gw, alice, gwMPT(500)));
1373 env(pay(gw, bob, gwMPT(500)));
1374 env.close();
1375
1376 env(delegate::set(alice, bob, {"PaymentMint", "PaymentBurn"}));
1377 env.close();
1378
1379 env(pay(alice, gw2, gwMPT(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1380 }
1381 }
1382
1383 // IOU issuer is an endpoint, but no trustline exists.
1384 {
1385 Env env(*this);
1386 Account const alice{"alice"};
1387 Account const bob{"bob"};
1388 Account const gw{"gateway"};
1389
1390 env.fund(XRP(10000), alice, bob, gw);
1391 env.close();
1392
1393 env(delegate::set(alice, bob, {"PaymentMint", "PaymentBurn"}));
1394 env.close();
1395
1396 env(pay(alice, gw, alice["USD"](50)),
1397 delegate::As(bob),
1399 }
1400
1401 // Both trust limits (who is the designated issuer) and balance direction
1402 // (which way DirectStepI executes) must be checked. Neither alone is sufficient.
1403 {
1404 Env env(*this);
1405 Account const alice{"alice"};
1406 Account const bob{"bob"};
1407 Account const gw{"gateway"};
1408 auto const gwUSD = gw["USD"];
1409 auto const aliceUSD = alice["USD"];
1410
1411 env.fund(XRP(10000), alice, bob, gw);
1412
1413 // Alice trusts gw but holds zero gw-issued USD. With balance == 0,
1414 // DirectStepI would issue rather than redeem, so PaymentBurn must
1415 // be rejected even though Alice's trust limit to gw is positive.
1416 {
1417 env.trust(gwUSD(200), alice);
1418 env.close();
1419
1420 // Alice has nothing to burn.
1421 env(delegate::set(alice, bob, {"PaymentBurn"}));
1422 env.close();
1423
1424 env(pay(alice, gw, gwUSD(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1425 env(pay(alice, gw, aliceUSD(50)),
1426 delegate::As(bob),
1428 }
1429
1430 // Set up a trust line where gw holds alice-issued USD. DirectStepI
1431 // would redeem rather than issue, so PaymentMint must be rejected
1432 // even though the endpoint identity matches.
1433 {
1434 // Gw sets trust to accept alice-issued USD.
1435 env(trust(gw, aliceUSD(200)));
1436 env.close();
1437
1438 // Alice issues her own USD to gw; now gw holds alice's IOUs.
1439 env(pay(alice, gw, aliceUSD(100)));
1440 env.close();
1441
1442 // In gw's view, accountHolds(gw, USD, alice) > 0, so DirectStepI redeems.
1443 // PaymentMint must be rejected because the step would redeem, not issue.
1444 env(delegate::set(gw, bob, {"PaymentMint"}));
1445 env.close();
1446
1447 env(pay(gw, alice, gwUSD(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1448 env(pay(gw, alice, aliceUSD(50)),
1449 delegate::As(bob),
1451 }
1452 }
1453
1454 // Alice trusts gw but gw is not willing to hold alice's IOU (destLimit == 0).
1455 {
1456 Env env(*this);
1457 Account const alice{"alice"};
1458 Account const bob{"bob"};
1459 Account const gw{"gateway"};
1460 env.fund(XRP(10000), alice, bob, gw);
1461 env.trust(gw["USD"](200), alice);
1462 env.close();
1463
1464 env(delegate::set(alice, bob, {"PaymentMint"}));
1465 env.close();
1466
1467 env(pay(alice, gw, gw["USD"](50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1468 env(pay(alice, gw, alice["USD"](50)),
1469 delegate::As(bob),
1471 }
1472
1473 // Verify granular permissions of different tx types in the same SLE are scoped
1474 // correctly. AccountSet permissions don't apply to Payment and vice versa
1475 {
1476 Env env(*this);
1477 Account const alice{"alice"};
1478 Account const bob{"bob"};
1479 Account const gw{"gw"};
1480 auto const usd = gw["USD"];
1481 env.fund(XRP(10000), alice, bob, gw);
1482 env.trust(usd(200), alice);
1483 env.close();
1484
1485 // Alice granted bob with both AccountDomainSet and PaymentMint.
1486 env(delegate::set(alice, bob, {"AccountDomainSet", "PaymentMint"}));
1487 env.close();
1488
1489 // PaymentMint fails at granular semantic check because alice is not the issuer.
1490 env(pay(alice, gw, usd(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1491
1492 // AccountDomainSet applies correctly to AccountSet
1493 std::string const domain = "example.com";
1494 auto jt = noop(alice);
1495 jt[sfDomain] = strHex(domain);
1496 jt[sfDelegate] = bob.human();
1497 env(jt);
1498 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
1499
1500 // gw gives bob PaymentMint and bob can mint on gw's behalf
1501 env(delegate::set(gw, bob, {"PaymentMint"}));
1502 env.close();
1503 env(pay(gw, alice, usd(50)), delegate::As(bob));
1504 env.require(Balance(alice, usd(50)));
1505 }
1506 }
1507
1508 void
1510 {
1511 testcase("test TrustSet granular permissions");
1512 using namespace jtx;
1513
1514 // test TrustlineUnfreeze, TrustlineFreeze and TrustlineAuthorize
1515 {
1516 Env env(*this);
1517 Account const gw{"gw"};
1518 Account const alice{"alice"};
1519 Account const bob{"bob"};
1520 env.fund(XRP(10000), gw, alice, bob);
1521 env(fset(gw, asfRequireAuth));
1522 env.close();
1523
1524 env(delegate::set(alice, bob, {"TrustlineUnfreeze"}));
1525 env.close();
1526 // bob can not create trustline on behalf of alice because he only
1527 // has unfreeze permission
1528 env(trust(alice, gw["USD"](50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1529 env.close();
1530
1531 // alice creates trustline by herself
1532 env(trust(alice, gw["USD"](50)));
1533 env.close();
1534
1535 // gw gives bob unfreeze permission
1536 env(delegate::set(gw, bob, {"TrustlineUnfreeze"}));
1537 env.close();
1538
1539 // unsupported flags
1540 env(trust(alice, gw["USD"](50), tfSetNoRipple),
1541 delegate::As(bob),
1543 env(trust(alice, gw["USD"](50), tfClearNoRipple),
1544 delegate::As(bob),
1546 env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze),
1547 delegate::As(bob),
1549 env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze),
1550 delegate::As(bob),
1552 env.close();
1553
1554 // supported flags with wrong permission
1555 env(trust(gw, gw["USD"](0), alice, tfSetfAuth),
1556 delegate::As(bob),
1558 env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
1559 delegate::As(bob),
1561 env.close();
1562
1563 env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
1564 env.close();
1565 env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
1566 delegate::As(bob),
1568 env.close();
1569 // although trustline authorize is granted, bob can not change the
1570 // limit number
1571 env(trust(gw, gw["USD"](50), alice, tfSetfAuth),
1572 delegate::As(bob),
1574 env.close();
1575
1576 // supported flags with correct permission
1577 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::As(bob));
1578 env.close();
1579 env(delegate::set(gw, bob, {"TrustlineAuthorize", "TrustlineFreeze"}));
1580 env.close();
1581 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::As(bob));
1582 env.close();
1583 env(delegate::set(gw, bob, {"TrustlineAuthorize", "TrustlineUnfreeze"}));
1584 env.close();
1585 env(trust(gw, gw["USD"](0), alice, tfClearFreeze), delegate::As(bob));
1586 env.close();
1587 // but bob can not freeze trustline because he no longer has freeze
1588 // permission
1589 env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
1590 delegate::As(bob),
1592
1593 // cannot update LimitAmount with granular permission, both high and
1594 // low account
1595 env(trust(alice, gw["USD"](100)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1596 env(trust(gw, alice["USD"](100)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1597
1598 // can not set QualityIn or QualityOut
1599 auto tx = trust(alice, gw["USD"](50));
1600 tx["QualityIn"] = "1000";
1602 auto tx2 = trust(alice, gw["USD"](50));
1603 tx2["QualityOut"] = "1000";
1605 auto tx3 = trust(gw, alice["USD"](50));
1606 tx3["QualityIn"] = "1000";
1608 auto tx4 = trust(gw, alice["USD"](50));
1609 tx4["QualityOut"] = "1000";
1611
1612 // granting TrustSet can make it work
1613 env(delegate::set(gw, bob, {"TrustSet"}));
1614 env.close();
1615 auto tx5 = trust(gw, alice["USD"](50));
1616 tx5["QualityOut"] = "1000";
1617 env(tx5, delegate::As(bob));
1618 auto tx6 = trust(alice, gw["USD"](50));
1619 tx6["QualityOut"] = "1000";
1621 env(delegate::set(alice, bob, {"TrustSet"}));
1622 env.close();
1623 env(tx6, delegate::As(bob));
1624 }
1625
1626 // test mix of transaction level delegation and granular delegation
1627 {
1628 Env env(*this);
1629 Account const gw{"gw"};
1630 Account const alice{"alice"};
1631 Account const bob{"bob"};
1632 env.fund(XRP(10000), gw, alice, bob);
1633 env(fset(gw, asfRequireAuth));
1634 env.close();
1635
1636 // bob does not have permission
1637 env(trust(alice, gw["USD"](50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1638 env(delegate::set(alice, bob, {"TrustlineUnfreeze", "NFTokenCreateOffer"}));
1639 env.close();
1640 // bob still does not have permission
1641 env(trust(alice, gw["USD"](50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1642
1643 // add TrustSet permission and some unrelated permission
1644 env(delegate::set(
1645 alice,
1646 bob,
1647 {"TrustlineUnfreeze", "NFTokenCreateOffer", "TrustSet", "AccountTransferRateSet"}));
1648 env.close();
1649 env(trust(alice, gw["USD"](50)), delegate::As(bob));
1650 env.close();
1651
1652 env(delegate::set(
1653 gw,
1654 bob,
1655 {"TrustlineUnfreeze", "NFTokenCreateOffer", "TrustSet", "AccountTransferRateSet"}));
1656 env.close();
1657
1658 // since bob has TrustSet permission, he does not need
1659 // TrustlineFreeze granular permission to freeze the trustline
1660 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::As(bob));
1661 env(trust(gw, gw["USD"](0), alice, tfClearFreeze), delegate::As(bob));
1662 // bob can perform all the operations regarding TrustSet
1663 env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::As(bob));
1664 env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze), delegate::As(bob));
1665 env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze), delegate::As(bob));
1666 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::As(bob));
1667 env(trust(alice, gw["USD"](50), tfSetNoRipple), delegate::As(bob));
1668 env(trust(alice, gw["USD"](50), tfClearNoRipple), delegate::As(bob));
1669 }
1670
1671 // tfFullyCanonicalSig won't block delegated transaction
1672 {
1673 Env env(*this);
1674 Account const gw{"gw"};
1675 Account const alice{"alice"};
1676 Account const bob{"bob"};
1677 env.fund(XRP(10000), gw, alice, bob);
1678 env(fset(gw, asfRequireAuth));
1679 env.close();
1680 env(trust(alice, gw["USD"](50)));
1681 env.close();
1682
1683 env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
1684 env.close();
1685 env(trust(gw, gw["USD"](0), alice, tfSetfAuth | tfFullyCanonicalSig),
1686 delegate::As(bob));
1687 }
1688
1689 {
1690 Env env(*this);
1691 Account const gw{"gw"};
1692 Account const alice{"alice"};
1693 Account const bob{"bob"};
1694 env.fund(XRP(10000), gw, alice, bob);
1695
1696 env(fset(gw, asfRequireAuth));
1697 env.close();
1698 env(trust(alice, gw["USD"](50)));
1699 env.close();
1700 env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
1701 env.close();
1702
1703 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::As(bob));
1704 env.close();
1705
1706 // sfQualityOut is a valid TrustSet field, but not permitted in granular template
1707 json::Value txJson = trust(gw, gw["USD"](0), alice, tfSetfAuth);
1708 txJson[sfQualityOut.jsonName] = 100;
1709 env(txJson, delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1710
1711 // tfSetNoRipple is a valid flag for TrustSet, but not permitted in granular template
1712 env(trust(gw, gw["USD"](0), alice, tfSetfAuth | tfSetNoRipple),
1713 delegate::As(bob),
1715 }
1716 }
1717
1718 void
1720 {
1721 testcase("test AccountSet granular permissions");
1722 using namespace jtx;
1723
1724 // test AccountDomainSet, AccountEmailHashSet,
1725 // AccountMessageKeySet,AccountTransferRateSet, and AccountTickSizeSet
1726 // granular permissions
1727 {
1728 Env env(*this);
1729 auto const alice = Account{"alice"};
1730 auto const bob = Account{"bob"};
1731 env.fund(XRP(10000), alice, bob);
1732 env.close();
1733
1734 // alice gives bob some random permission, which is not related to
1735 // the AccountSet transaction
1736 env(delegate::set(alice, bob, {"TrustlineUnfreeze"}));
1737 env.close();
1738
1739 // bob does not have permission to set domain
1740 // on behalf of alice
1741 std::string const domain = "example.com";
1742 auto jt = noop(alice);
1743 jt[sfDomain] = strHex(domain);
1744 jt[sfDelegate] = bob.human();
1745
1746 // add granular permission related to AccountSet but is not the
1747 // correct permission for domain set
1748 env(delegate::set(alice, bob, {"TrustlineUnfreeze", "AccountEmailHashSet"}));
1749 env.close();
1751
1752 // alice give granular permission of AccountDomainSet to bob
1753 env(delegate::set(alice, bob, {"AccountDomainSet"}));
1754 env.close();
1755
1756 // bob set account domain on behalf of alice
1757 env(jt);
1758 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
1759
1760 // bob can reset domain
1761 jt[sfDomain] = "";
1762 env(jt);
1763 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfDomain));
1764
1765 // bob tries to set unauthorized flag, it will fail
1766 std::string const failDomain = "fail_domain_update";
1767 jt[sfFlags] = tfRequireAuth;
1768 jt[sfDomain] = strHex(failDomain);
1770 // reset flag number
1771 jt[sfFlags] = 0;
1772
1773 // bob tries to update domain and set email hash,
1774 // but he does not have permission to set email hash
1775 jt[sfDomain] = strHex(domain);
1776 std::string const mh("5F31A79367DC3137FADA860C05742EE6");
1777 jt[sfEmailHash] = mh;
1779
1780 // alice give granular permission of AccountEmailHashSet to bob
1781 env(delegate::set(alice, bob, {"AccountDomainSet", "AccountEmailHashSet"}));
1782 env.close();
1783 env(jt);
1784 BEAST_EXPECT(to_string((*env.le(alice))[sfEmailHash]) == mh);
1785 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
1786
1787 // bob does not have permission to set message key for alice
1788 auto const rkp = randomKeyPair(KeyType::Ed25519);
1789 jt[sfMessageKey] = strHex(rkp.first.slice());
1791
1792 // alice give granular permission of AccountMessageKeySet to bob
1793 env(delegate::set(
1794 alice, bob, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"}));
1795 env.close();
1796
1797 // bob can set message key for alice
1798 env(jt);
1799 BEAST_EXPECT(strHex((*env.le(alice))[sfMessageKey]) == strHex(rkp.first.slice()));
1800 jt[sfMessageKey] = "";
1801 env(jt);
1802 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfMessageKey));
1803
1804 // bob does not have permission to set transfer rate for alice
1805 env(rate(alice, 2.0), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1806
1807 // alice give granular permission of AccountTransferRateSet to bob
1808 env(delegate::set(
1809 alice,
1810 bob,
1811 {"AccountDomainSet",
1812 "AccountEmailHashSet",
1813 "AccountMessageKeySet",
1814 "AccountTransferRateSet"}));
1815 env.close();
1816 auto jtRate = rate(alice, 2.0);
1817 jtRate[sfDelegate] = bob.human();
1818 env(jtRate, delegate::As(bob));
1819 BEAST_EXPECT((*env.le(alice))[sfTransferRate] == 2000000000);
1820
1821 // bob does not have permission to set ticksize for alice
1822 jt[sfTickSize] = 8;
1824
1825 // alice give granular permission of AccountTickSizeSet to bob
1826 env(delegate::set(
1827 alice,
1828 bob,
1829 {"AccountDomainSet",
1830 "AccountEmailHashSet",
1831 "AccountMessageKeySet",
1832 "AccountTransferRateSet",
1833 "AccountTickSizeSet"}));
1834 env.close();
1835 env(jt);
1836 BEAST_EXPECT((*env.le(alice))[sfTickSize] == 8);
1837
1838 // can not set asfRequireAuth flag for alice
1839 env(fset(alice, asfRequireAuth), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1840
1841 // reset Delegate will delete the Delegate
1842 // object
1843 env(delegate::set(alice, bob, {}));
1844 // bib still does not have permission to set asfRequireAuth for
1845 // alice
1846 env(fset(alice, asfRequireAuth), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1847 // alice can set for herself
1848 env(fset(alice, asfRequireAuth));
1849 env.require(Flags(alice, asfRequireAuth));
1850 env.close();
1851
1852 // can not update tick size because bob no longer has permission
1853 jt[sfTickSize] = 7;
1855
1856 env(delegate::set(
1857 alice, bob, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"}));
1858 env.close();
1859
1860 // bob does not have permission to set wallet locater for alice
1861 std::string const locator =
1862 "9633EC8AF54F16B5286DB1D7B519EF49EEFC050C0C8AC4384F1D88ACD1BFDF"
1863 "05";
1864 auto jv2 = noop(alice);
1865 jv2[sfDomain] = strHex(domain);
1866 jv2[sfDelegate] = bob.human();
1867 jv2[sfWalletLocator] = locator;
1868 env(jv2, Ter(terNO_DELEGATE_PERMISSION));
1869 }
1870
1871 // can not set AccountSet flags on behalf of other account,
1872 // in permissions.macro, the template for AccountSet does
1873 // not allow any flag set or clear.
1874 {
1875 Env env(*this);
1876 auto const alice = Account{"alice"};
1877 auto const bob = Account{"bob"};
1878 env.fund(XRP(10000), alice, bob);
1879 env.close();
1880
1881 auto testSetClearFlag = [&](std::uint32_t flag) {
1882 // bob can not set flag on behalf of alice
1883 env(fset(alice, flag), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1884 // alice set by herself
1885 env(fset(alice, flag));
1886 env.close();
1887 env.require(Flags(alice, flag));
1888 // bob can not clear on behalf of alice
1889 env(fclear(alice, flag), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1890 };
1891
1892 // testSetClearFlag(asfNoFreeze);
1893 testSetClearFlag(asfRequireAuth);
1894 testSetClearFlag(asfAllowTrustLineClawback);
1895
1896 // alice gives some granular permissions to bob
1897 env(delegate::set(
1898 alice, bob, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"}));
1899 env.close();
1900
1901 testSetClearFlag(asfDefaultRipple);
1902 testSetClearFlag(asfDepositAuth);
1903 testSetClearFlag(asfDisallowIncomingCheck);
1904 testSetClearFlag(asfDisallowIncomingNFTokenOffer);
1905 testSetClearFlag(asfDisallowIncomingPayChan);
1906 testSetClearFlag(asfDisallowIncomingTrustline);
1907 testSetClearFlag(asfDisallowXRP);
1908 testSetClearFlag(asfRequireDest);
1909 testSetClearFlag(asfGlobalFreeze);
1910
1911 // bob can not set asfAccountTxnID on behalf of alice
1912 env(fset(alice, asfAccountTxnID), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1913 env(fset(alice, asfAccountTxnID));
1914 env.close();
1915 BEAST_EXPECT(env.le(alice)->isFieldPresent(sfAccountTxnID));
1916 env(fclear(alice, asfAccountTxnID), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1917
1918 // bob can not set asfAuthorizedNFTokenMinter on behalf of alice
1919 json::Value jt = fset(alice, asfAuthorizedNFTokenMinter);
1920 jt[sfDelegate] = bob.human();
1921 jt[sfNFTokenMinter] = bob.human();
1923
1924 // bob gives alice some permissions
1925 env(delegate::set(
1926 bob, alice, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"}));
1927 env.close();
1928
1929 // since we can not set asfNoFreeze if asfAllowTrustLineClawback is
1930 // set, which can not be clear either. Test alice set asfNoFreeze on
1931 // behalf of bob.
1932 env(fset(alice, asfNoFreeze), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1933 env(fset(bob, asfNoFreeze));
1934 env.close();
1935 env.require(Flags(bob, asfNoFreeze));
1936 // alice can not clear on behalf of bob
1937 env(fclear(alice, asfNoFreeze), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
1938
1939 // bob can not set asfDisableMaster on behalf of alice
1940 Account const bobKey{"bobKey", KeyType::Secp256k1};
1941 env(regkey(bob, bobKey));
1942 env.close();
1943 env(fset(alice, asfDisableMaster),
1944 delegate::As(bob),
1945 Sig(bob),
1947 }
1948
1949 // tfFullyCanonicalSig won't block delegated transaction
1950 {
1951 Env env(*this);
1952 Account const alice{"alice"};
1953 Account const bob{"bob"};
1954 env.fund(XRP(10000), alice, bob);
1955 env.close();
1956
1957 env(delegate::set(alice, bob, {"AccountDomainSet", "AccountEmailHashSet"}));
1958 env.close();
1959
1960 std::string const domain = "example.com";
1961 auto jt = noop(alice);
1962 jt[sfDomain] = strHex(domain);
1963 jt[sfDelegate] = bob.human();
1964 jt[sfFlags] = tfFullyCanonicalSig;
1965
1966 env(jt);
1967 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
1968 }
1969
1970 // setting invalid field not in permissions.macro template will be rejected.
1971 {
1972 Env env(*this);
1973 auto const alice = Account{"alice"};
1974 auto const bob = Account{"bob"};
1975 env.fund(XRP(10000), alice, bob);
1976 env.close();
1977
1978 // Alice gives Bob permission to set her Domain
1979 env(delegate::set(alice, bob, {"AccountDomainSet"}));
1980 env.close();
1981
1982 std::string const domain = "example.com";
1983 auto txJson = noop(alice);
1984 txJson[sfDomain] = strHex(domain);
1985 txJson[sfDelegate] = bob.human();
1986
1987 // sfNFTokenMinter is a valid field in AccountSet tx, but
1988 // it is not permitted for granular template
1989 txJson[sfNFTokenMinter] = bob.human();
1990
1991 env(txJson, Ter(terNO_DELEGATE_PERMISSION));
1992 }
1993
1994 // Delegated AccountSet with no fields and no flags is allowed,
1995 // because it is allowed in the non-delegated case as well.
1996 {
1997 Env env(*this);
1998 Account const alice{"alice"};
1999 Account const bob{"bob"};
2000 env.fund(XRP(10000), alice, bob);
2001 env.close();
2002
2003 env(delegate::set(alice, bob, {"AccountDomainSet"}));
2004 env.close();
2005
2006 auto jt = noop(alice);
2007 jt[sfDelegate] = bob.human();
2008 env(jt);
2009 }
2010
2011 // Revoking all permissions deletes the SLE and subsequent attempts are rejected.
2012 {
2013 Env env(*this);
2014 Account const alice{"alice"};
2015 Account const bob{"bob"};
2016 env.fund(XRP(10000), alice, bob);
2017 env.close();
2018
2019 env(delegate::set(alice, bob, {"AccountDomainSet"}));
2020 env.close();
2021
2022 std::string const domain = "example.com";
2023 auto jt = noop(alice);
2024 jt[sfDomain] = strHex(domain);
2025 jt[sfDelegate] = bob.human();
2026 env(jt);
2027
2028 // empty DelegateSet deletes the SLE
2029 env(delegate::set(alice, bob, {}));
2030 env.close();
2031
2033 }
2034 }
2035
2036 void
2038 {
2039 testcase("test MPTokenIssuanceSet granular");
2040 using namespace jtx;
2041
2042 // test MPTokenIssuanceUnlock and MPTokenIssuanceLock permissions
2043 {
2044 Env env(*this);
2045 Account const alice{"alice"};
2046 Account const bob{"bob"};
2047 env.fund(XRP(100000), alice, bob);
2048 env.close();
2049
2050 MPTTester mpt(env, alice, {.fund = false});
2051 env.close();
2052 mpt.create({.flags = tfMPTCanLock});
2053 env.close();
2054
2055 // delegate ledger object is not created yet
2056 mpt.set(
2057 {.account = alice,
2058 .flags = tfMPTLock,
2059 .delegate = bob,
2061
2062 // alice gives granular permission to bob of MPTokenIssuanceUnlock
2063 env(delegate::set(alice, bob, {"MPTokenIssuanceUnlock"}));
2064 env.close();
2065 // bob does not have lock permission
2066 mpt.set(
2067 {.account = alice,
2068 .flags = tfMPTLock,
2069 .delegate = bob,
2071 // bob now has lock permission, but does not have unlock permission
2072 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
2073 env.close();
2074 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
2075 mpt.set(
2076 {.account = alice,
2077 .flags = tfMPTUnlock,
2078 .delegate = bob,
2080
2081 // now bob can lock and unlock
2082 env(delegate::set(alice, bob, {"MPTokenIssuanceLock", "MPTokenIssuanceUnlock"}));
2083 env.close();
2084 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
2085 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
2086 env.close();
2087 }
2088
2089 // test mix of granular and transaction level permission
2090 {
2091 Env env(*this);
2092 Account const alice{"alice"};
2093 Account const bob{"bob"};
2094 env.fund(XRP(100000), alice, bob);
2095 env.close();
2096
2097 MPTTester mpt(env, alice, {.fund = false});
2098 env.close();
2099 mpt.create({.flags = tfMPTCanLock});
2100 env.close();
2101
2102 // alice gives granular permission to bob of MPTokenIssuanceLock
2103 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
2104 env.close();
2105 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
2106 // bob does not have unlock permission
2107 mpt.set(
2108 {.account = alice,
2109 .flags = tfMPTUnlock,
2110 .delegate = bob,
2112
2113 // alice gives bob some unrelated permission with
2114 // MPTokenIssuanceLock
2115 env(delegate::set(alice, bob, {"NFTokenMint", "MPTokenIssuanceLock", "NFTokenBurn"}));
2116 env.close();
2117 // bob can not unlock
2118 mpt.set(
2119 {.account = alice,
2120 .flags = tfMPTUnlock,
2121 .delegate = bob,
2123
2124 // alice add MPTokenIssuanceSet to permissions
2125 env(delegate::set(
2126 alice,
2127 bob,
2128 {"NFTokenMint", "MPTokenIssuanceLock", "NFTokenBurn", "MPTokenIssuanceSet"}));
2129 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
2130 // alice can lock by herself
2131 mpt.set({.account = alice, .flags = tfMPTLock});
2132 mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
2133 mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
2134 }
2135
2136 // tfFullyCanonicalSig won't block delegated transaction
2137 {
2138 Env env(*this);
2139 Account const alice{"alice"};
2140 Account const bob{"bob"};
2141 env.fund(XRP(100000), alice, bob);
2142 env.close();
2143
2144 MPTTester mpt(env, alice, {.fund = false});
2145 env.close();
2146 mpt.create({.flags = tfMPTCanLock});
2147 env.close();
2148
2149 // alice gives granular permission to bob of MPTokenIssuanceLock
2150 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
2151 env.close();
2152 mpt.set({.account = alice, .flags = tfMPTLock | tfFullyCanonicalSig, .delegate = bob});
2153 }
2154
2155 // field not permitted to exist in granular delegation
2156 {
2157 Env env(*this);
2158 Account const alice{"alice"};
2159 Account const bob{"bob"};
2160 env.fund(XRP(100000), alice, bob);
2161
2162 MPTTester mpt(env, alice, {.fund = false});
2163 mpt.create({.flags = tfMPTCanLock});
2164 env.close();
2165
2166 // alice gives granular permission to bob for MPTokenIssuanceLock
2167 env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
2168 env.close();
2169
2170 // Field is not permitted, permitted fields for delegation is defined in
2171 // permissions.macro.
2172 mpt.set(
2173 {.account = alice,
2174 .mutableFlags = 2,
2175 .delegate = bob,
2177
2178 // Notice: flags not defined in permissions.macro are not permitted for delegation.
2179 // Since preflight will check invalid flag for the tx, it is not reachable.
2180 // If any new flag is defined into the transaction in the future,
2181 // but is not allowed for delegation, the transaction will be rejected with
2182 // terNO_DELEGATE_PERMISSION. The set of permitted flags for delegation is defined in
2183 // permissions.macro.
2184 }
2185 }
2186
2187 void
2189 {
2190 testcase("test single sign");
2191 using namespace jtx;
2192
2193 Env env(*this);
2194 Account const alice{"alice"};
2195 Account const bob{"bob"};
2196 Account const carol{"carol"};
2197 env.fund(XRP(100000), alice, bob, carol);
2198 env.close();
2199
2200 env(delegate::set(alice, bob, {"Payment"}));
2201 env.close();
2202
2203 auto aliceBalance = env.balance(alice);
2204 auto bobBalance = env.balance(bob);
2205 auto carolBalance = env.balance(carol);
2206
2207 env(pay(alice, carol, XRP(100)), Fee(XRP(10)), delegate::As(bob), Sig(bob));
2208 env.close();
2209 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100));
2210 BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10));
2211 BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(100));
2212 }
2213
2214 void
2216 {
2217 testcase("test single sign with bad secret");
2218 using namespace jtx;
2219
2220 {
2221 Env env(*this);
2222 Account const alice{"alice"};
2223 Account const bob{"bob"};
2224 Account const carol{"carol"};
2225 env.fund(XRP(100000), alice, bob, carol);
2226 env.close();
2227
2228 env(delegate::set(alice, bob, {"Payment"}));
2229 env.close();
2230
2231 auto aliceBalance = env.balance(alice);
2232 auto bobBalance = env.balance(bob);
2233 auto carolBalance = env.balance(carol);
2234
2235 env(pay(alice, carol, XRP(100)),
2236 Fee(XRP(10)),
2237 delegate::As(bob),
2238 Sig(alice),
2239 Ter(tefBAD_AUTH));
2240 env.close();
2241 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2242 BEAST_EXPECT(env.balance(bob) == bobBalance);
2243 BEAST_EXPECT(env.balance(carol) == carolBalance);
2244 }
2245
2246 {
2247 Env env(*this);
2248 Account const alice{"alice"};
2249 Account const bob{"bob"};
2250 Account const carol{"carol"};
2251 env.fund(XRP(100000), alice, bob, carol);
2252 env.close();
2253
2254 env(delegate::set(alice, bob, {"TrustSet"}));
2255 env.close();
2256
2257 auto aliceBalance = env.balance(alice);
2258 auto bobBalance = env.balance(bob);
2259 auto carolBalance = env.balance(carol);
2260
2261 env(pay(alice, carol, XRP(100)),
2262 Fee(XRP(10)),
2263 delegate::As(bob),
2264 Sig(carol),
2266 env.close();
2267 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2268 BEAST_EXPECT(env.balance(bob) == bobBalance);
2269 BEAST_EXPECT(env.balance(carol) == carolBalance);
2270
2271 env(pay(alice, carol, XRP(100)),
2272 Fee(XRP(10)),
2273 delegate::As(bob),
2274 Sig(alice),
2276 env.close();
2277 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2278 BEAST_EXPECT(env.balance(bob) == bobBalance);
2279 BEAST_EXPECT(env.balance(carol) == carolBalance);
2280 }
2281
2282 {
2283 Env env(*this);
2284 Account const alice{"alice"};
2285 Account const bob{"bob"};
2286 Account const carol{"carol"};
2287 env.fund(XRP(100000), alice, bob, carol);
2288 env.close();
2289
2290 auto aliceBalance = env.balance(alice);
2291 auto bobBalance = env.balance(bob);
2292 auto carolBalance = env.balance(carol);
2293
2294 env(pay(alice, carol, XRP(100)),
2295 Fee(XRP(10)),
2296 delegate::As(bob),
2297 Sig(alice),
2299 env.close();
2300 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2301 BEAST_EXPECT(env.balance(bob) == bobBalance);
2302 BEAST_EXPECT(env.balance(carol) == carolBalance);
2303
2304 env(pay(alice, carol, XRP(100)),
2305 Fee(XRP(10)),
2306 delegate::As(bob),
2307 Sig(carol),
2309 env.close();
2310 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2311 BEAST_EXPECT(env.balance(bob) == bobBalance);
2312 BEAST_EXPECT(env.balance(carol) == carolBalance);
2313 }
2314 }
2315
2316 void
2318 {
2319 testcase("test multi sign");
2320 using namespace jtx;
2321
2322 Env env(*this);
2323 Account const alice{"alice"};
2324 Account const bob{"bob"};
2325 Account const carol{"carol"};
2326 Account const daria{"daria"};
2327 Account const edward{"edward"};
2328 env.fund(XRP(100000), alice, bob, carol, daria, edward);
2329 env.close();
2330
2331 env(signers(bob, 2, {{daria, 1}, {edward, 1}}));
2332 env.close();
2333
2334 env(delegate::set(alice, bob, {"Payment"}));
2335 env.close();
2336
2337 auto aliceBalance = env.balance(alice);
2338 auto bobBalance = env.balance(bob);
2339 auto carolBalance = env.balance(carol);
2340 auto dariaBalance = env.balance(daria);
2341 auto edwardBalance = env.balance(edward);
2342
2343 env(pay(alice, carol, XRP(100)), Fee(XRP(10)), delegate::As(bob), Msig(daria, edward));
2344 env.close();
2345 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100));
2346 BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10));
2347 BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(100));
2348 BEAST_EXPECT(env.balance(daria) == dariaBalance);
2349 BEAST_EXPECT(env.balance(edward) == edwardBalance);
2350 }
2351
2352 void
2354 {
2355 testcase("test multi sign which does not meet quorum");
2356 using namespace jtx;
2357
2358 Env env(*this);
2359 Account const alice{"alice"};
2360 Account const bob{"bob"};
2361 Account const carol{"carol"};
2362 Account const daria{"daria"};
2363 Account const edward{"edward"};
2364 Account const fred{"fred"};
2365 env.fund(XRP(100000), alice, bob, carol, daria, edward, fred);
2366 env.close();
2367
2368 env(signers(bob, 3, {{daria, 1}, {edward, 1}, {fred, 1}}));
2369 env.close();
2370
2371 env(delegate::set(alice, bob, {"Payment"}));
2372 env.close();
2373
2374 auto aliceBalance = env.balance(alice);
2375 auto bobBalance = env.balance(bob);
2376 auto carolBalance = env.balance(carol);
2377 auto dariaBalance = env.balance(daria);
2378 auto edwardBalance = env.balance(edward);
2379
2380 env(pay(alice, carol, XRP(100)),
2381 Fee(XRP(10)),
2382 delegate::As(bob),
2383 Msig(daria, edward),
2385 env.close();
2386 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2387 BEAST_EXPECT(env.balance(bob) == bobBalance);
2388 BEAST_EXPECT(env.balance(carol) == carolBalance);
2389 BEAST_EXPECT(env.balance(daria) == dariaBalance);
2390 BEAST_EXPECT(env.balance(edward) == edwardBalance);
2391 }
2392
2393 void
2395 {
2396 // checkMultiSign disallows the owner of the account to
2397 // be part of the multisigner list. When it is a delegated transaction,
2398 // the delegate account should not be part of the multisigner list.
2399 testcase("test delegator as multisigner in delegate's signer list");
2400 using namespace jtx;
2401
2402 Env env(*this);
2403 Account const alice{"alice"};
2404 Account const bob{"bob"};
2405 Account const carol{"carol"};
2406 Account const daria{"daria"};
2407 env.fund(XRP(100000), alice, bob, carol, daria);
2408 env.close();
2409
2410 env(delegate::set(alice, bob, {"Payment"}));
2411 env.close();
2412
2413 // bob's signer list includes the delegator alice and daria
2414 env(signers(bob, 2, {{alice, 1}, {daria, 1}}));
2415 env.close();
2416
2417 auto aliceBalance = env.balance(alice);
2418 auto bobBalance = env.balance(bob);
2419 auto carolBalance = env.balance(carol);
2420 auto const amt = 100;
2421
2422 // alice can sign as a multisigner for bob
2423 env(pay(alice, carol, XRP(100)), Fee(XRP(10)), delegate::As(bob), Msig(alice, daria));
2424 env.close();
2425
2426 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(amt));
2427 BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10));
2428 BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(amt));
2429
2430 // alice can not sign as a multisigner if she sent the transaction by herself.
2431 env(pay(alice, carol, XRP(100)), Fee(XRP(10)), Msig(alice, daria), Ter(telENV_RPC_FAILED));
2432 env.close();
2433
2434 // Get new balances
2435 aliceBalance = env.balance(alice);
2436 bobBalance = env.balance(bob);
2437 carolBalance = env.balance(carol);
2438
2439 // bob (the delegate) should not appear as a multisigner in his transaction sent on behalf
2440 // of alice. STTx::checkMultiSign catches this at the local-check stage, so the jtx
2441 // framework returns telENV_RPC_FAILED.
2442 env(pay(alice, carol, XRP(50)),
2443 Fee(XRP(10)),
2444 delegate::As(bob),
2445 Msig(alice, bob),
2447 env.close();
2448 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2449 BEAST_EXPECT(env.balance(bob) == bobBalance);
2450 BEAST_EXPECT(env.balance(carol) == carolBalance);
2451 }
2452
2453 void
2455 {
2456 // In sortAndValidateSigners, if it is a delegated transaction, the delegate account is
2457 // the forbidden account from appearing in its own Signers array.
2458 testcase("test sign_for with delegated transaction");
2459 using namespace jtx;
2460
2461 Env env(*this);
2462 Account const alice{"alice"};
2463 Account const bob{"bob"};
2464 Account const carol{"carol"};
2465 Account const daria{"daria"};
2466 env.fund(XRP(100000), alice, bob, carol, daria);
2467 env.close();
2468
2469 env(delegate::set(alice, bob, {"Payment"}));
2470 env.close();
2471
2472 // bob's signer list includes the delegator alice and daria
2473 env(signers(bob, 2, {{alice, 1}, {daria, 1}}));
2474 env.close();
2475
2476 auto const baseFee = env.current()->fees().base;
2477
2478 auto const sendAmt = 1'000'000;
2479 auto makeDelegateTx = [&]() -> json::Value {
2480 json::Value jv;
2481 jv[jss::tx_json][jss::Account] = alice.human();
2482 jv[jss::tx_json][sfDelegate.jsonName] = bob.human();
2483 jv[jss::tx_json][jss::TransactionType] = jss::Payment;
2484 jv[jss::tx_json][jss::Destination] = carol.human();
2485 jv[jss::tx_json][jss::Amount] = sendAmt;
2486 jv[jss::tx_json][jss::Fee] = std::to_string((10 * baseFee).drops());
2487 jv[jss::tx_json][jss::Sequence] = env.seq(alice);
2488 jv[jss::tx_json][jss::SigningPubKey] = "";
2489 return jv;
2490 };
2491
2492 // The delegator alice and daria both sign via sign_for, which is valid
2493 {
2494 auto const aliceBalance = env.balance(alice);
2495 auto const bobBalance = env.balance(bob);
2496 auto const dariaBalance = env.balance(daria);
2497 auto const carolBalance = env.balance(carol);
2498
2499 json::Value jv = makeDelegateTx();
2500 jv[jss::account] = alice.human();
2501 jv[jss::secret] = alice.name();
2502 auto jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result];
2503 BEAST_EXPECT(jrr[jss::status] == "success");
2504
2505 json::Value jv2;
2506 jv2[jss::tx_json] = jrr[jss::tx_json];
2507 jv2[jss::account] = daria.human();
2508 jv2[jss::secret] = daria.name();
2509 jrr = env.rpc("json", "sign_for", to_string(jv2))[jss::result];
2510 BEAST_EXPECT(jrr[jss::status] == "success");
2511
2512 json::Value jvSubmit;
2513 jvSubmit[jss::tx_json] = jrr[jss::tx_json];
2514 jrr = env.rpc("json", "submit_multisigned", to_string(jvSubmit))[jss::result];
2515 BEAST_EXPECT(jrr[jss::status] == "success");
2516 env.close();
2517 BEAST_EXPECT(env.balance(alice) == aliceBalance - XRPAmount(sendAmt));
2518 BEAST_EXPECT(env.balance(bob) == bobBalance - (10 * baseFee));
2519 BEAST_EXPECT(env.balance(daria) == dariaBalance);
2520 BEAST_EXPECT(env.balance(carol) == carolBalance + XRPAmount(sendAmt));
2521 }
2522
2523 // The delegated account bob attempts sign_for, will be rejected.
2524 {
2525 json::Value jv = makeDelegateTx();
2526 jv[jss::account] = bob.human();
2527 jv[jss::secret] = bob.name();
2528 auto jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result];
2529 BEAST_EXPECT(jrr[jss::status] == "error");
2530 BEAST_EXPECT(
2531 jrr[jss::error_message].asString().contains(
2532 "A Signer may not be the transaction's Account"));
2533 }
2534 }
2535
2536 void
2538 {
2539 testcase("test permission value");
2540 using namespace jtx;
2541
2542 Env env(*this, features);
2543
2544 Account const alice{"alice"};
2545 Account const bob{"bob"};
2546 env.fund(XRP(100000), alice, bob);
2547 env.close();
2548
2549 auto buildRequest = [&](auto value) -> json::Value {
2550 json::Value jv;
2551 jv[jss::TransactionType] = jss::DelegateSet;
2552 jv[jss::Account] = alice.human();
2553 jv[sfAuthorize.jsonName] = bob.human();
2554
2555 json::Value permissionsJson(json::ValueType::Array);
2556 json::Value permissionValue;
2557 permissionValue[sfPermissionValue.jsonName] = value;
2558 json::Value permissionObj;
2559 permissionObj[sfPermission.jsonName] = permissionValue;
2560 permissionsJson.append(permissionObj);
2561 jv[sfPermissions.jsonName] = permissionsJson;
2562
2563 return jv;
2564 };
2565
2566 // invalid permission value.
2567 // neither granular permission nor transaction level permission
2568 for (auto value : {0, 100000, 54321})
2569 {
2570 auto jv = buildRequest(value);
2571 env(jv, Ter(temMALFORMED));
2572 }
2573 }
2574
2575 void
2577 {
2578 testcase("test delegate disabled tx");
2579 using namespace jtx;
2580
2581 // map of tx and required feature.
2582 // non-delegable tx are not included.
2583 // NFTokenMint, NFTokenBurn, NFTokenCreateOffer, NFTokenCancelOffer,
2584 // NFTokenAcceptOffer are not included, they are tested separately.
2586 {"AMMClawback", featureAMMClawback},
2587 {"AMMCreate", featureAMM},
2588 {"AMMDeposit", featureAMM},
2589 {"AMMWithdraw", featureAMM},
2590 {"AMMVote", featureAMM},
2591 {"AMMBid", featureAMM},
2592 {"AMMDelete", featureAMM},
2593 {"XChainCreateClaimID", featureXChainBridge},
2594 {"XChainCommit", featureXChainBridge},
2595 {"XChainClaim", featureXChainBridge},
2596 {"XChainAccountCreateCommit", featureXChainBridge},
2597 {"XChainAddClaimAttestation", featureXChainBridge},
2598 {"XChainAddAccountCreateAttestation", featureXChainBridge},
2599 {"XChainModifyBridge", featureXChainBridge},
2600 {"XChainCreateBridge", featureXChainBridge},
2601 {"DIDSet", featureDID},
2602 {"DIDDelete", featureDID},
2603 {"OracleSet", featurePriceOracle},
2604 {"OracleDelete", featurePriceOracle},
2605 {"LedgerStateFix", fixNFTokenPageLinks},
2606 {"MPTokenIssuanceCreate", featureMPTokensV1},
2607 {"MPTokenIssuanceDestroy", featureMPTokensV1},
2608 {"MPTokenIssuanceSet", featureMPTokensV1},
2609 {"MPTokenAuthorize", featureMPTokensV1},
2610 {"CredentialCreate", featureCredentials},
2611 {"CredentialAccept", featureCredentials},
2612 {"CredentialDelete", featureCredentials},
2613 {"NFTokenModify", featureDynamicNFT},
2614 {"PermissionedDomainSet", featurePermissionedDomains},
2615 {"PermissionedDomainDelete", featurePermissionedDomains}};
2616
2617 // Can not delegate tx if any required feature disabled.
2618 {
2619 auto txAmendmentDisabled = [&](FeatureBitset features, std::string const& tx) {
2620 BEAST_EXPECT(txRequiredFeatures.contains(tx));
2621
2622 Env env(*this, features - txRequiredFeatures[tx]);
2623
2624 Account const alice{"alice"};
2625 Account const bob{"bob"};
2626 env.fund(XRP(100000), alice, bob);
2627 env.close();
2628
2629 env(delegate::set(alice, bob, {tx}), Ter(temMALFORMED));
2630 };
2631
2632 for (auto const& tx : txRequiredFeatures)
2633 txAmendmentDisabled(features, tx.first);
2634 }
2635
2636 // if all the required features in txRequiredFeatures are enabled, will
2637 // succeed
2638 {
2639 auto txAmendmentEnabled = [&](std::string const& tx) {
2640 Env env(*this, features);
2641
2642 Account const alice{"alice"};
2643 Account const bob{"bob"};
2644 env.fund(XRP(100000), alice, bob);
2645 env.close();
2646
2647 env(delegate::set(alice, bob, {tx}));
2648 };
2649
2650 for (auto const& tx : txRequiredFeatures)
2651 txAmendmentEnabled(tx.first);
2652 }
2653
2654 // Granular permissions also require the amendment for their underlying
2655 // transaction type.
2656 {
2657 for (auto const permission : {"MPTokenIssuanceLock", "MPTokenIssuanceUnlock"})
2658 {
2659 Env env(*this, features - featureMPTokensV1);
2660
2661 Account const alice{"alice"};
2662 Account const bob{"bob"};
2663 env.fund(XRP(100000), alice, bob);
2664 env.close();
2665
2666 env(delegate::set(alice, bob, {permission}), Ter(temMALFORMED));
2667 }
2668 }
2669 }
2670
2671 void
2673 {
2674 testcase("Make sure GranularSandbox is checked after transaction-level permission");
2675
2676 using namespace jtx;
2677
2678 Env env(*this);
2679 Account const gw{"gw"};
2680 Account const alice{"alice"};
2681 Account const bob{"bob"};
2682 env.fund(XRP(10000), gw, alice, bob);
2683
2684 env(fset(gw, asfRequireAuth));
2685 env.close();
2686 env(trust(alice, gw["USD"](50)));
2687 env.close();
2688 env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
2689 env.close();
2690
2691 env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::As(bob));
2692 env.close();
2693
2694 // sfQualityOut is a valid TrustSet field, but not permitted in granular template
2695 json::Value txJson = trust(gw, gw["USD"](0), alice, tfSetfAuth);
2696 txJson[sfQualityOut.jsonName] = 100;
2697 env(txJson, delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
2698
2699 // Now Alice grants Bob with transaction level permission
2700 env(delegate::set(gw, bob, {"TrustlineAuthorize", "TrustSet"}));
2701 env.close();
2702
2703 // NOTE: This case is to ensure that if a delegate possesses a
2704 // transaction-level permission (e.g., TrustSet), the granular sandbox must not incorrectly
2705 // block the transaction. The function checkGranularSandbox MUST be called after the
2706 // transaction-level permission check. This test case is to avoid future refactor mistakes,
2707 // modifying the order will fail here.
2708 env(txJson, delegate::As(bob));
2709 }
2710
2711 void
2713 {
2714 testcase("Delegable Transactions Completeness");
2715
2716 std::size_t delegableCount = 0;
2717
2718#pragma push_macro("TRANSACTION")
2719#undef TRANSACTION
2720
2721#define TRANSACTION(tag, value, name, txDelegable, ...) \
2722 if (txDelegable == xrpl::Delegable) \
2723 { \
2724 delegableCount++; \
2725 }
2726
2727#include <xrpl/protocol/detail/transactions.macro>
2728
2729#undef TRANSACTION
2730#pragma pop_macro("TRANSACTION")
2731
2732 // ====================================================================
2733 // IMPORTANT NOTICE:
2734 //
2735 // If this test fails, it indicates that the 'Delegation::delegable' status
2736 // in transactions.macro has been changed. Delegation allows accounts to act
2737 // on behalf of others, significantly increasing the security surface.
2738 //
2739 //
2740 // To ENSURE any added transaction is safe and compatible with delegation:
2741 //
2742 // 1. Verify that the transaction is intended to be delegable.
2743 // 2. Every standard test case for that transaction MUST be
2744 // duplicated and verified for a Delegated context.
2745 // 3. Ensure that Fee, Reserve, and Signing are correctly handled.
2746 //
2747 // DO NOT modify expectedDelegableCount unless all scenarios, including
2748 // edge cases, have been fully tested and verified.
2749 // ====================================================================
2750 // Includes the five confidential MPT transaction types, which are
2751 // explicitly marked Delegable in transactions.macro.
2752 std::size_t const expectedDelegableCount = 56;
2753
2754 BEAST_EXPECTS(
2755 delegableCount == expectedDelegableCount,
2756 "\n[SECURITY] New delegable transaction detected!"
2757 "\n Expected: " +
2758 std::to_string(expectedDelegableCount) +
2759 "\n Actual: " + std::to_string(delegableCount) +
2760 "\n Action: Verify security requirements to interact with Delegation feature");
2761 }
2762
2763 void
2765 {
2766 testcase("non-delegable tx with sfDelegate is rejected at preflight");
2767 using namespace jtx;
2768
2769 Env env(*this, features);
2770 Account const alice{"alice"};
2771 Account const bob{"bob"};
2772 env.fund(XRP(10000), alice, bob);
2773 env.close();
2774
2775 // Transactions that are notDelegable and have no granular permissions
2776 // will be rejected with temINVALID at preflight.
2777 // Note: pseudo-transactions (EnableAmendment, SetFee and UNLModify) are also
2778 // notDelegable but are excluded here — passesLocalChecks() blocks them
2779 // before preflight1 is ever reached.
2780 {
2781 // SetRegularKey, SignerListSet, AccountDelete, DelegateSet.
2782 env(regkey(alice, bob), delegate::As(bob), Ter(temINVALID));
2783 env(signers(alice, 1, {{bob, 1}}), delegate::As(bob), Ter(temINVALID));
2784 env(acctdelete(alice, bob), delegate::As(bob), Ter(temINVALID));
2785 env(delegate::set(alice, bob, {"Payment"}), delegate::As(bob), Ter(temINVALID));
2786
2787 // SAV transactions.
2788 {
2789 Vault const vault{env};
2790 auto [createTx, keylet] = vault.create({.owner = alice, .asset = xrpIssue()});
2791 env(createTx, delegate::As(bob), Ter(temINVALID));
2792
2793 env(vault.set({.owner = alice, .id = keylet.key}),
2794 delegate::As(bob),
2795 Ter(temINVALID));
2796 env(vault.del({.owner = alice, .id = keylet.key}),
2797 delegate::As(bob),
2798 Ter(temINVALID));
2799 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = XRP(1)}),
2800 delegate::As(bob),
2801 Ter(temINVALID));
2802 env(vault.withdraw({.depositor = alice, .id = keylet.key, .amount = XRP(1)}),
2803 delegate::As(bob),
2804 Ter(temINVALID));
2805 env(vault.clawback({.issuer = alice, .id = keylet.key, .holder = bob}),
2806 delegate::As(bob),
2807 Ter(temINVALID));
2808 }
2809
2810 // Batch transaction: the outer Batch itself is non-delegable.
2811 {
2812 auto const seq = env.seq(alice);
2813 auto const batchFee = batch::calcBatchFee(env, 0, 1);
2814 env(batch::outer(alice, seq, batchFee, tfAllOrNothing),
2815 batch::Inner(pay(alice, bob, XRP(1)), seq + 1),
2816 delegate::As(bob),
2817 Ter(temINVALID));
2818 }
2819
2820 // Lending protocol transactions
2821 {
2822 Vault const vault{env};
2823 auto [createTx, keylet] = vault.create({.owner = alice, .asset = xrpIssue()});
2824 env(createTx);
2825
2826 env(loanBroker::set(alice, keylet.key), delegate::As(bob), Ter(temINVALID));
2827 env(loanBroker::del(alice, keylet.key), delegate::As(bob), Ter(temINVALID));
2828 env(loanBroker::coverDeposit(alice, keylet.key, XRP(1)),
2829 delegate::As(bob),
2830 Ter(temINVALID));
2831 env(loanBroker::coverWithdraw(alice, keylet.key, XRP(1)),
2832 delegate::As(bob),
2833 Ter(temINVALID));
2835
2836 env(loan::set(alice, keylet.key, Number(100)), delegate::As(bob), Ter(temINVALID));
2837 env(loan::manage(alice, keylet.key, 0), delegate::As(bob), Ter(temINVALID));
2838 env(loan::del(alice, keylet.key), delegate::As(bob), Ter(temINVALID));
2839 env(loan::pay(alice, keylet.key, XRP(1)), delegate::As(bob), Ter(temINVALID));
2840 }
2841 }
2842
2843 // AccountSet is notDelegable at tx level but has granular permissions,
2844 // so sfDelegate passes preflight and is rejected at invokeCheckPermission with
2845 // terNO_DELEGATE_PERMISSION.
2846 {
2847 env(fset(alice, asfDefaultRipple), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
2848 }
2849 }
2850
2851 void
2853 {
2854 testcase("DelegateUtils nullptr check");
2855
2856 // checkTxPermission nullptr check
2857 STTx const tx{ttPAYMENT, [](STObject&) {}};
2858 BEAST_EXPECT(checkTxPermission(nullptr, tx) == terNO_DELEGATE_PERMISSION);
2859
2860 // getGranularPermission nullptr check
2861 auto const granularPermissions = getGranularPermission(nullptr, ttPAYMENT);
2862 BEAST_EXPECT(granularPermissions.empty());
2863 }
2864
2865 void
2867 {
2868 testcase("test Permission to Tx type");
2869
2870 // 0 is not a valid permission value
2871 BEAST_EXPECT(!Permission::permissionToTxType(0));
2872
2873 // 1 maps to Payment transaction
2874 BEAST_EXPECT(Permission::permissionToTxType(1) == ttPAYMENT);
2875
2876 // UINT16_MAX+1 is the maximum possible tx-level permission value
2877 constexpr uint32_t maxTxPermission = std::numeric_limits<uint16_t>::max() + 1u;
2878 BEAST_EXPECT(Permission::permissionToTxType(maxTxPermission).has_value());
2879
2880 // exceeding maximum value should return nullopt
2881 BEAST_EXPECT(!Permission::permissionToTxType(maxTxPermission + 1));
2882
2883 // All granular permission values should return nullopt since they do not map to a TxType.
2884 for (auto const gp : {
2885#pragma push_macro("GRANULAR_PERMISSION")
2886#undef GRANULAR_PERMISSION
2887#define GRANULAR_PERMISSION(type, txType, value, ...) GranularPermissionType::type,
2888#include <xrpl/protocol/detail/permissions.macro>
2889#undef GRANULAR_PERMISSION
2890#pragma pop_macro("GRANULAR_PERMISSION")
2891 })
2892 {
2893 BEAST_EXPECT(!Permission::permissionToTxType(static_cast<uint32_t>(gp)));
2894 }
2895 }
2896
2897 void
2929};
2931} // namespace xrpl::test
T any_of(T... args)
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Represents a JSON value.
Definition json_value.h:130
Value & append(Value const &value)
Append value to array at the end.
A class that simplifies iterating ledger directory pages.
Definition Dir.h:21
ConstIterator begin() const
Definition Dir.cpp:26
ConstIterator end() const
Definition Dir.cpp:44
Number is a floating point type that can represent a wide range of values.
Definition Number.h:306
static std::optional< TxType > permissionToTxType(std::uint32_t value)
void testNonDelegableTxWithDelegate(FeatureBitset features)
void testFeatureDisabled(FeatureBitset features)
void testInvalidRequest(FeatureBitset features)
void run() override
Runs the suite.
void testPaymentGranular(FeatureBitset features)
void testPermissionValue(FeatureBitset features)
void testTxRequireFeatures(FeatureBitset features)
Immutable cryptographic account descriptor.
Definition jtx/Account.h:17
std::string const & human() const
Returns the human readable public key.
Definition jtx/Account.h:92
std::string const & name() const
Return the name.
Definition jtx/Account.h:61
AccountID id() const
Returns the Account ID.
Definition jtx/Account.h:85
Sets the DeliverMin on a JTx.
Definition delivermin.h:13
A transaction testing environment.
Definition Env.h:143
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
SLE::const_pointer le(Account const &account) const
Return an account root.
Definition Env.cpp:284
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition Env.cpp:127
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:296
PrettyAmount limit(Account const &account, Issue const &issue) const
Returns the IOU limit on an account.
Definition Env.cpp:254
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:275
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:864
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:201
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:327
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:605
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:353
Set the fee on a JTx.
Definition fee.h:15
Match set account flags.
Definition flags.h:109
Test helper for creating, mutating, and asserting MPT and confidential MPT ledger state.
Definition mpt.h:385
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:256
Set a multisignature on a JTx.
Definition multisign.h:39
Add a path.
Definition paths.h:39
Sets the SendMax on a JTx.
Definition sendmax.h:13
Set the regular signature on a JTx.
Definition sig.h:13
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:13
Set the flags on a JTx.
Definition txflags.h:9
Adds an inner Batch transaction to a JTx and autofills it.
Definition batch.h:58
Sets the optional URI on a DIDSet.
Definition did.h:36
T contains(T... args)
T make_tuple(T... args)
T make_unique(T... args)
T max(T... args)
@ Array
array value (ordered list)
Definition json_value.h:25
Keylet computation functions.
Definition Indexes.h:34
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:357
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:186
json::Value outer(jtx::Account const &account, uint32_t seq, STAmount const &fee, std::uint32_t flags)
Build an outer Batch transaction JSON object.
Definition batch.cpp:53
XRPAmount calcBatchFee(jtx::Env const &env, uint32_t const &numSigners, uint32_t const &txns=0)
Calculate the expected outer Batch transaction fee.
Definition batch.cpp:35
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:41
json::Value set(jtx::Account const &account, jtx::Account const &authorize, std::vector< std::string > const &permissions)
Definition delegate.cpp:17
json::Value set(jtx::Account const &account)
Definition dids.cpp:14
json::Value set(AccountID const &account, uint256 const &vaultId, uint32_t flags)
json::Value coverWithdraw(AccountID const &account, uint256 const &brokerID, STAmount const &amount, uint32_t flags)
json::Value coverDeposit(AccountID const &account, uint256 const &brokerID, STAmount const &amount, uint32_t flags)
json::Value coverClawback(AccountID const &account, std::uint32_t flags)
json::Value del(AccountID const &account, uint256 const &brokerID, uint32_t flags)
json::Value set(AccountID const &account, uint256 const &loanBrokerID, Number principalRequested, std::uint32_t flags)
json::Value manage(AccountID const &account, uint256 const &loanID, std::uint32_t flags)
json::Value del(AccountID const &account, uint256 const &loanID, std::uint32_t flags)
json::Value pay(AccountID const &account, uint256 const &loanID, STAmount const &amount, std::uint32_t flags)
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
json::Value regkey(Account const &account, DisabledT)
Disable the regular key.
Definition regkey.cpp:13
bool expectOffers(Env &env, AccountID const &account, std::uint16_t size, std::vector< Amounts > const &toMatch)
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
json::Value noop(Account const &account)
The null transaction.
Definition noop.h:9
XRPAmount txFee(Env const &env, std::uint16_t n)
json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:102
FeatureBitset testableAmendments()
Definition Env.h:76
json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:14
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:18
json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:15
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
json::Value signers(Account const &account, std::uint32_t quorum, std::vector< Signer > const &v)
Definition multisign.cpp:31
json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:15
static XRPAmount reserve(jtx::Env &env, std::uint32_t count)
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
STTx createTx(bool disabling, LedgerIndex seq, PublicKey const &txKey)
Create ttUNL_MODIFY Tx.
constexpr XRPAmount
Convert XRP to drops (integral types).
Definition TxTest.h:48
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ telENV_RPC_FAILED
Definition TER.h:52
@ terINSUF_FEE_B
Definition TER.h:208
@ terNO_DELEGATE_PERMISSION
Definition TER.h:222
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:97
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
std::unordered_set< GranularPermissionType > getGranularPermission(SLE::const_ref delegate, TxType const &type)
Load the granular permissions granted to the delegate account for the specified transaction type.
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
BaseUInt< 256 > Domain
Domain is a 256-bit hash representing a specific domain.
Definition UintTypes.h:47
@ tefBAD_QUORUM
Definition TER.h:170
@ tefBAD_AUTH
Definition TER.h:159
NotTEC checkTxPermission(SLE::const_ref delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
@ temARRAY_TOO_LARGE
Definition TER.h:127
@ temBAD_FEE
Definition TER.h:78
@ temINVALID
Definition TER.h:96
@ temMALFORMED
Definition TER.h:73
@ temDISABLED
Definition TER.h:100
@ temREDUNDANT
Definition TER.h:98
@ temBAD_SIGNER
Definition TER.h:101
@ tecUNFUNDED_PAYMENT
Definition TER.h:283
@ tecNO_ENTRY
Definition TER.h:304
@ tecNO_TARGET
Definition TER.h:302
@ tecINSUFFICIENT_RESERVE
Definition TER.h:305
@ tecNO_PERMISSION
Definition TER.h:303
BaseUInt< 256 > uint256
Definition base_uint.h:562
@ tesSUCCESS
Definition TER.h:240
constexpr FlagValue tfFullyCanonicalSig
Definition TxFlags.h:42
std::enable_if_t< std::is_same_v< T, char >||std::is_same_v< T, unsigned char >, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:215
T to_string(T... args)