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