rippled
Loading...
Searching...
No Matches
DepositAuth_test.cpp
1#include <test/jtx.h>
2
3#include <xrpl/protocol/Feature.h>
4
5#include <algorithm>
6
7namespace ripple {
8namespace test {
9
10// Helper function that returns the reserve on an account based on
11// the passed in number of owners.
12static XRPAmount
14{
15 return env.current()->fees().accountReserve(count);
16}
17
18// Helper function that returns true if acct has the lsfDepostAuth flag set.
19static bool
20hasDepositAuth(jtx::Env const& env, jtx::Account const& acct)
21{
22 return ((*env.le(acct))[sfFlags] & lsfDepositAuth) == lsfDepositAuth;
23}
24
26{
27 void
29 {
30 testcase("Enable");
31
32 using namespace jtx;
33 Account const alice{"alice"};
34
35 {
36 // featureDepositAuth is disabled.
37 Env env(*this, testable_amendments() - featureDepositAuth);
38 env.fund(XRP(10000), alice);
39
40 // Note that, to support old behavior, invalid flags are ignored.
41 env(fset(alice, asfDepositAuth));
42 env.close();
43 BEAST_EXPECT(!hasDepositAuth(env, alice));
44
45 env(fclear(alice, asfDepositAuth));
46 env.close();
47 BEAST_EXPECT(!hasDepositAuth(env, alice));
48 }
49 {
50 // featureDepositAuth is enabled.
51 Env env(*this);
52 env.fund(XRP(10000), alice);
53
54 env(fset(alice, asfDepositAuth));
55 env.close();
56 BEAST_EXPECT(hasDepositAuth(env, alice));
57
58 env(fclear(alice, asfDepositAuth));
59 env.close();
60 BEAST_EXPECT(!hasDepositAuth(env, alice));
61 }
62 }
63
64 void
66 {
67 // Exercise IOU payments and non-direct XRP payments to an account
68 // that has the lsfDepositAuth flag set.
69 testcase("Pay IOU");
70
71 using namespace jtx;
72 Account const alice{"alice"};
73 Account const bob{"bob"};
74 Account const carol{"carol"};
75 Account const gw{"gw"};
76 IOU const USD = gw["USD"];
77
78 Env env(*this);
79
80 env.fund(XRP(10000), alice, bob, carol, gw);
81 env.close();
82 env.trust(USD(1000), alice, bob);
83 env.close();
84
85 env(pay(gw, alice, USD(150)));
86 env(offer(carol, USD(100), XRP(100)));
87 env.close();
88
89 // Make sure bob's trust line is all set up so he can receive USD.
90 env(pay(alice, bob, USD(50)));
91 env.close();
92
93 // bob sets the lsfDepositAuth flag.
95 env.close();
96
97 // None of the following payments should succeed.
98 auto failedIouPayments = [this, &env, &alice, &bob, &USD]() {
99 env.require(flags(bob, asfDepositAuth));
100
101 // Capture bob's balances before hand to confirm they don't change.
102 PrettyAmount const bobXrpBalance{env.balance(bob, XRP)};
103 PrettyAmount const bobUsdBalance{env.balance(bob, USD)};
104
105 env(pay(alice, bob, USD(50)), ter(tecNO_PERMISSION));
106 env.close();
107
108 // Note that even though alice is paying bob in XRP, the payment
109 // is still not allowed since the payment passes through an offer.
110 env(pay(alice, bob, drops(1)),
111 sendmax(USD(1)),
113 env.close();
114
115 BEAST_EXPECT(bobXrpBalance == env.balance(bob, XRP));
116 BEAST_EXPECT(bobUsdBalance == env.balance(bob, USD));
117 };
118
119 // Test when bob has an XRP balance > base reserve.
120 failedIouPayments();
121
122 // Set bob's XRP balance == base reserve. Also demonstrate that
123 // bob can make payments while his lsfDepositAuth flag is set.
124 env(pay(bob, alice, USD(25)));
125 env.close();
126
127 {
128 STAmount const bobPaysXRP{env.balance(bob, XRP) - reserve(env, 1)};
129 XRPAmount const bobPaysFee{reserve(env, 1) - reserve(env, 0)};
130 env(pay(bob, alice, bobPaysXRP), fee(bobPaysFee));
131 env.close();
132 }
133
134 // Test when bob's XRP balance == base reserve.
135 BEAST_EXPECT(env.balance(bob, XRP) == reserve(env, 0));
136 BEAST_EXPECT(env.balance(bob, USD) == USD(25));
137 failedIouPayments();
138
139 // Test when bob has an XRP balance == 0.
140 env(noop(bob), fee(reserve(env, 0)));
141 env.close();
142
143 BEAST_EXPECT(env.balance(bob, XRP) == XRP(0));
144 failedIouPayments();
145
146 // Give bob enough XRP for the fee to clear the lsfDepositAuth flag.
147 env(pay(alice, bob, drops(env.current()->fees().base)));
148
149 // bob clears the lsfDepositAuth and the next payment succeeds.
150 env(fclear(bob, asfDepositAuth));
151 env.close();
152
153 env(pay(alice, bob, USD(50)));
154 env.close();
155
156 env(pay(alice, bob, drops(1)), sendmax(USD(1)));
157 env.close();
158 }
159
160 void
162 {
163 // Exercise direct XRP payments to an account that has the
164 // lsfDepositAuth flag set.
165 testcase("Pay XRP");
166
167 using namespace jtx;
168 Account const alice{"alice"};
169 Account const bob{"bob"};
170
171 Env env(*this);
172 auto const baseFee = env.current()->fees().base;
173
174 env.fund(XRP(10000), alice, bob);
175 env.close();
176
177 // bob sets the lsfDepositAuth flag.
178 env(fset(bob, asfDepositAuth), fee(drops(baseFee)));
179 env.close();
180 BEAST_EXPECT(env.balance(bob, XRP) == XRP(10000) - drops(baseFee));
181
182 // bob has more XRP than the base reserve. Any XRP payment should fail.
183 env(pay(alice, bob, drops(1)), ter(tecNO_PERMISSION));
184 env.close();
185 BEAST_EXPECT(env.balance(bob, XRP) == XRP(10000) - drops(baseFee));
186
187 // Change bob's XRP balance to exactly the base reserve.
188 {
189 STAmount const bobPaysXRP{env.balance(bob, XRP) - reserve(env, 1)};
190 XRPAmount const bobPaysFee{reserve(env, 1) - reserve(env, 0)};
191 env(pay(bob, alice, bobPaysXRP), fee(bobPaysFee));
192 env.close();
193 }
194
195 // bob has exactly the base reserve. A small enough direct XRP
196 // payment should succeed.
197 BEAST_EXPECT(env.balance(bob, XRP) == reserve(env, 0));
198 env(pay(alice, bob, drops(1)));
199 env.close();
200
201 // bob has exactly the base reserve + 1. No payment should succeed.
202 BEAST_EXPECT(env.balance(bob, XRP) == reserve(env, 0) + drops(1));
203 env(pay(alice, bob, drops(1)), ter(tecNO_PERMISSION));
204 env.close();
205
206 // Take bob down to a balance of 0 XRP.
207 env(noop(bob), fee(reserve(env, 0) + drops(1)));
208 env.close();
209 BEAST_EXPECT(env.balance(bob, XRP) == drops(0));
210
211 // We should not be able to pay bob more than the base reserve.
212 env(pay(alice, bob, reserve(env, 0) + drops(1)), ter(tecNO_PERMISSION));
213 env.close();
214
215 // However a payment of exactly the base reserve should succeed.
216 env(pay(alice, bob, reserve(env, 0) + drops(0)));
217 env.close();
218 BEAST_EXPECT(env.balance(bob, XRP) == reserve(env, 0));
219
220 // We should be able to pay bob the base reserve one more time.
221 env(pay(alice, bob, reserve(env, 0) + drops(0)));
222 env.close();
223 BEAST_EXPECT(
224 env.balance(bob, XRP) == (reserve(env, 0) + reserve(env, 0)));
225
226 // bob's above the threshold again. Any payment should fail.
227 env(pay(alice, bob, drops(1)), ter(tecNO_PERMISSION));
228 env.close();
229 BEAST_EXPECT(
230 env.balance(bob, XRP) == (reserve(env, 0) + reserve(env, 0)));
231
232 // Take bob back down to a zero XRP balance.
233 env(noop(bob), fee(env.balance(bob, XRP)));
234 env.close();
235 BEAST_EXPECT(env.balance(bob, XRP) == drops(0));
236
237 // bob should not be able to clear lsfDepositAuth.
239 env.close();
240
241 // We should be able to pay bob 1 drop now.
242 env(pay(alice, bob, drops(1)));
243 env.close();
244 BEAST_EXPECT(env.balance(bob, XRP) == drops(1));
245
246 // Pay bob enough so he can afford the fee to clear lsfDepositAuth.
247 env(pay(alice, bob, drops(baseFee - 1)));
248 env.close();
249
250 // Interestingly, at this point the terINSUF_FEE_B retry grabs the
251 // request to clear lsfDepositAuth. So the balance should be zero
252 // and lsfDepositAuth should be cleared.
253 BEAST_EXPECT(env.balance(bob, XRP) == drops(0));
254 env.require(nflags(bob, asfDepositAuth));
255
256 // Since bob no longer has lsfDepositAuth set we should be able to
257 // pay him more than the base reserve.
258 env(pay(alice, bob, reserve(env, 0) + drops(1)));
259 env.close();
260 BEAST_EXPECT(env.balance(bob, XRP) == reserve(env, 0) + drops(1));
261 }
262
263 void
265 {
266 // It its current incarnation the DepositAuth flag does not change
267 // any behaviors regarding rippling and the NoRipple flag.
268 // Demonstrate that.
269 testcase("No Ripple");
270
271 using namespace jtx;
272 Account const gw1("gw1");
273 Account const gw2("gw2");
274 Account const alice("alice");
275 Account const bob("bob");
276
277 IOU const USD1(gw1["USD"]);
278 IOU const USD2(gw2["USD"]);
279
280 auto testIssuer = [&](FeatureBitset const& features,
281 bool noRipplePrev,
282 bool noRippleNext,
283 bool withDepositAuth) {
284 assert(!withDepositAuth || features[featureDepositAuth]);
285
286 Env env(*this, features);
287
288 env.fund(XRP(10000), gw1, alice, bob);
289 env.close();
290 env(trust(gw1, alice["USD"](10), noRipplePrev ? tfSetNoRipple : 0));
291 env(trust(gw1, bob["USD"](10), noRippleNext ? tfSetNoRipple : 0));
292 env.trust(USD1(10), alice, bob);
293
294 env(pay(gw1, alice, USD1(10)));
295
296 if (withDepositAuth)
297 env(fset(gw1, asfDepositAuth));
298
299 TER const result = (noRippleNext && noRipplePrev) ? TER{tecPATH_DRY}
300 : TER{tesSUCCESS};
301 env(pay(alice, bob, USD1(10)), path(gw1), ter(result));
302 };
303
304 auto testNonIssuer = [&](FeatureBitset const& features,
305 bool noRipplePrev,
306 bool noRippleNext,
307 bool withDepositAuth) {
308 assert(!withDepositAuth || features[featureDepositAuth]);
309
310 Env env(*this, features);
311
312 env.fund(XRP(10000), gw1, gw2, alice);
313 env.close();
314 env(trust(alice, USD1(10), noRipplePrev ? tfSetNoRipple : 0));
315 env(trust(alice, USD2(10), noRippleNext ? tfSetNoRipple : 0));
316 env(pay(gw2, alice, USD2(10)));
317
318 if (withDepositAuth)
319 env(fset(alice, asfDepositAuth));
320
321 TER const result = (noRippleNext && noRipplePrev) ? TER{tecPATH_DRY}
322 : TER{tesSUCCESS};
323 env(pay(gw1, gw2, USD2(10)),
324 path(alice),
325 sendmax(USD1(10)),
326 ter(result));
327 };
328
329 // Test every combo of noRipplePrev, noRippleNext, and withDepositAuth
330 for (int i = 0; i < 8; ++i)
331 {
332 auto const noRipplePrev = i & 0x1;
333 auto const noRippleNext = i & 0x2;
334 auto const withDepositAuth = i & 0x4;
335 testIssuer(
336 testable_amendments() | featureDepositAuth,
337 noRipplePrev,
338 noRippleNext,
339 withDepositAuth);
340
341 if (!withDepositAuth)
342 testIssuer(
343 testable_amendments() - featureDepositAuth,
344 noRipplePrev,
345 noRippleNext,
346 withDepositAuth);
347
348 testNonIssuer(
349 testable_amendments() | featureDepositAuth,
350 noRipplePrev,
351 noRippleNext,
352 withDepositAuth);
353
354 if (!withDepositAuth)
355 testNonIssuer(
356 testable_amendments() - featureDepositAuth,
357 noRipplePrev,
358 noRippleNext,
359 withDepositAuth);
360 }
361 }
362
363 void
364 run() override
365 {
366 testEnable();
367 testPayIOU();
368 testPayXRP();
369 testNoRipple();
370 }
371};
372
373static Json::Value
375 jtx::Env& env,
376 jtx::Account const& acc,
378{
379 Json::Value jvParams;
380 jvParams[jss::ledger_index] = jss::validated;
381 jvParams[jss::deposit_preauth][jss::owner] = acc.human();
382 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
384 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
385 for (auto const& o : auth)
386 {
387 arr.append(o.toLEJson());
388 }
389 return env.rpc("json", "ledger_entry", to_string(jvParams));
390}
391
393{
394 void
396 {
397 testcase("Enable");
398
399 using namespace jtx;
400 Account const alice{"alice"};
401 Account const becky{"becky"};
402 {
403 // featureDepositPreauth is disabled.
404 Env env(*this, testable_amendments() - featureDepositPreauth);
405 env.fund(XRP(10000), alice, becky);
406 env.close();
407
408 // Should not be able to add a DepositPreauth to alice.
409 env(deposit::auth(alice, becky), ter(temDISABLED));
410 env.close();
411 env.require(owners(alice, 0));
412 env.require(owners(becky, 0));
413
414 // Should not be able to remove a DepositPreauth from alice.
415 env(deposit::unauth(alice, becky), ter(temDISABLED));
416 env.close();
417 env.require(owners(alice, 0));
418 env.require(owners(becky, 0));
419 }
420 {
421 // featureDepositPreauth is enabled. The valid case is really
422 // simple:
423 // o We should be able to add and remove an entry, and
424 // o That entry should cost one reserve.
425 // o The reserve should be returned when the entry is removed.
426 Env env(*this);
427 env.fund(XRP(10000), alice, becky);
428 env.close();
429
430 // Add a DepositPreauth to alice.
431 env(deposit::auth(alice, becky));
432 env.close();
433 env.require(owners(alice, 1));
434 env.require(owners(becky, 0));
435
436 // Remove a DepositPreauth from alice.
437 env(deposit::unauth(alice, becky));
438 env.close();
439 env.require(owners(alice, 0));
440 env.require(owners(becky, 0));
441 }
442 {
443 // Verify that an account can be preauthorized and unauthorized
444 // using tickets.
445 Env env(*this);
446 env.fund(XRP(10000), alice, becky);
447 env.close();
448
449 env(ticket::create(alice, 2));
450 std::uint32_t const aliceSeq{env.seq(alice)};
451 env.close();
452 env.require(tickets(alice, 2));
453
454 // Consume the tickets from biggest seq to smallest 'cuz we can.
455 std::uint32_t aliceTicketSeq{env.seq(alice)};
456
457 // Add a DepositPreauth to alice.
458 env(deposit::auth(alice, becky), ticket::use(--aliceTicketSeq));
459 env.close();
460 // Alice uses a ticket but gains a preauth entry.
461 env.require(tickets(alice, 1));
462 env.require(owners(alice, 2));
463 BEAST_EXPECT(env.seq(alice) == aliceSeq);
464 env.require(owners(becky, 0));
465
466 // Remove a DepositPreauth from alice.
467 env(deposit::unauth(alice, becky), ticket::use(--aliceTicketSeq));
468 env.close();
469 env.require(tickets(alice, 0));
470 env.require(owners(alice, 0));
471 BEAST_EXPECT(env.seq(alice) == aliceSeq);
472 env.require(owners(becky, 0));
473 }
474 }
475
476 void
478 {
479 testcase("Invalid");
480
481 using namespace jtx;
482 Account const alice{"alice"};
483 Account const becky{"becky"};
484 Account const carol{"carol"};
485
486 Env env(*this);
487
488 // Tell env about alice, becky and carol since they are not yet funded.
489 env.memoize(alice);
490 env.memoize(becky);
491 env.memoize(carol);
492
493 // Add DepositPreauth to an unfunded account.
494 env(deposit::auth(alice, becky), seq(1), ter(terNO_ACCOUNT));
495
496 env.fund(XRP(10000), alice, becky);
497 env.close();
498
499 // Bad fee.
500 env(deposit::auth(alice, becky), fee(drops(-10)), ter(temBAD_FEE));
501 env.close();
502
503 // Bad flags.
504 env(deposit::auth(alice, becky), txflags(tfSell), ter(temINVALID_FLAG));
505 env.close();
506
507 {
508 // Neither auth not unauth.
509 Json::Value tx{deposit::auth(alice, becky)};
510 tx.removeMember(sfAuthorize.jsonName);
511 env(tx, ter(temMALFORMED));
512 env.close();
513 }
514 {
515 // Both auth and unauth.
516 Json::Value tx{deposit::auth(alice, becky)};
517 tx[sfUnauthorize.jsonName] = becky.human();
518 env(tx, ter(temMALFORMED));
519 env.close();
520 }
521 {
522 // Alice authorizes a zero account.
523 Json::Value tx{deposit::auth(alice, becky)};
524 tx[sfAuthorize.jsonName] = to_string(xrpAccount());
525 env(tx, ter(temINVALID_ACCOUNT_ID));
526 env.close();
527 }
528
529 // alice authorizes herself.
530 env(deposit::auth(alice, alice), ter(temCANNOT_PREAUTH_SELF));
531 env.close();
532
533 // alice authorizes an unfunded account.
534 env(deposit::auth(alice, carol), ter(tecNO_TARGET));
535 env.close();
536
537 // alice successfully authorizes becky.
538 env.require(owners(alice, 0));
539 env.require(owners(becky, 0));
540 env(deposit::auth(alice, becky));
541 env.close();
542 env.require(owners(alice, 1));
543 env.require(owners(becky, 0));
544
545 // alice attempts to create a duplicate authorization.
546 env(deposit::auth(alice, becky), ter(tecDUPLICATE));
547 env.close();
548 env.require(owners(alice, 1));
549 env.require(owners(becky, 0));
550
551 // carol attempts to preauthorize but doesn't have enough reserve.
552 env.fund(drops(249'999'999), carol);
553 env.close();
554
555 env(deposit::auth(carol, becky), ter(tecINSUFFICIENT_RESERVE));
556 env.close();
557 env.require(owners(carol, 0));
558 env.require(owners(becky, 0));
559
560 // carol gets enough XRP to (barely) meet the reserve.
561 env(pay(alice, carol, drops(env.current()->fees().base + 1)));
562 env.close();
563 env(deposit::auth(carol, becky));
564 env.close();
565 env.require(owners(carol, 1));
566 env.require(owners(becky, 0));
567
568 // But carol can't meet the reserve for another preauthorization.
569 env(deposit::auth(carol, alice), ter(tecINSUFFICIENT_RESERVE));
570 env.close();
571 env.require(owners(carol, 1));
572 env.require(owners(becky, 0));
573 env.require(owners(alice, 1));
574
575 // alice attempts to remove an authorization she doesn't have.
576 env(deposit::unauth(alice, carol), ter(tecNO_ENTRY));
577 env.close();
578 env.require(owners(alice, 1));
579 env.require(owners(becky, 0));
580
581 // alice successfully removes her authorization of becky.
582 env(deposit::unauth(alice, becky));
583 env.close();
584 env.require(owners(alice, 0));
585 env.require(owners(becky, 0));
586
587 // alice removes becky again and gets an error.
588 env(deposit::unauth(alice, becky), ter(tecNO_ENTRY));
589 env.close();
590 env.require(owners(alice, 0));
591 env.require(owners(becky, 0));
592 }
593
594 void
596 {
597 testcase("Payment");
598
599 using namespace jtx;
600 Account const alice{"alice"};
601 Account const becky{"becky"};
602 Account const gw{"gw"};
603 IOU const USD(gw["USD"]);
604
605 bool const supportsPreauth = {features[featureDepositPreauth]};
606
607 {
608 // The initial implementation of DepositAuth had a bug where an
609 // account with the DepositAuth flag set could not make a payment
610 // to itself. That bug was fixed in the DepositPreauth amendment.
611 Env env(*this, features);
612 env.fund(XRP(5000), alice, becky, gw);
613 env.close();
614
615 env.trust(USD(1000), alice);
616 env.trust(USD(1000), becky);
617 env.close();
618
619 env(pay(gw, alice, USD(500)));
620 env.close();
621
622 env(offer(alice, XRP(100), USD(100), tfPassive),
623 require(offers(alice, 1)));
624 env.close();
625
626 // becky pays herself USD (10) by consuming part of alice's offer.
627 // Make sure the payment works if PaymentAuth is not involved.
628 env(pay(becky, becky, USD(10)), path(~USD), sendmax(XRP(10)));
629 env.close();
630
631 // becky decides to require authorization for deposits.
632 env(fset(becky, asfDepositAuth));
633 env.close();
634
635 // becky pays herself again. Whether it succeeds depends on
636 // whether featureDepositPreauth is enabled.
637 TER const expect{
638 supportsPreauth ? TER{tesSUCCESS} : TER{tecNO_PERMISSION}};
639
640 env(pay(becky, becky, USD(10)),
641 path(~USD),
642 sendmax(XRP(10)),
643 ter(expect));
644 env.close();
645
646 {
647 // becky setup depositpreauth with credentials
648 char const credType[] = "abcde";
649 Account const carol{"carol"};
650 env.fund(XRP(5000), carol);
651 env.close();
652
653 bool const supportsCredentials = features[featureCredentials];
654
655 TER const expectCredentials(
656 supportsCredentials ? TER(tesSUCCESS) : TER(temDISABLED));
657 TER const expectPayment(
658 !supportsCredentials
660 : (!supportsPreauth ? TER(tecNO_PERMISSION)
661 : TER(tesSUCCESS)));
662 TER const expectDP(
663 !supportsPreauth
665 : (!supportsCredentials ? TER(temDISABLED)
666 : TER(tesSUCCESS)));
667
668 env(deposit::authCredentials(becky, {{carol, credType}}),
669 ter(expectDP));
670 env.close();
671
672 // gw accept credentials
673 env(credentials::create(gw, carol, credType),
674 ter(expectCredentials));
675 env.close();
676 env(credentials::accept(gw, carol, credType),
677 ter(expectCredentials));
678 env.close();
679
680 auto jv = credentials::ledgerEntry(env, gw, carol, credType);
681 std::string const credIdx = supportsCredentials
682 ? jv[jss::result][jss::index].asString()
683 : "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6"
684 "EA288BE4";
685
686 env(pay(gw, becky, USD(100)),
687 credentials::ids({credIdx}),
688 ter(expectPayment));
689 env.close();
690 }
691
692 {
693 using namespace std::chrono;
694
695 if (!supportsPreauth)
696 {
697 auto const seq1 = env.seq(alice);
698 env(escrow::create(alice, becky, XRP(100)),
699 escrow::finish_time(env.now() + 1s));
700 env.close();
701
702 // Failed as rule is disabled
703 env(escrow::finish(gw, alice, seq1),
704 fee(1500),
706 env.close();
707 }
708 }
709 }
710
711 if (supportsPreauth)
712 {
713 // Make sure DepositPreauthorization works for payments.
714
715 Account const carol{"carol"};
716
717 Env env(*this, features);
718 env.fund(XRP(5000), alice, becky, carol, gw);
719 env.close();
720
721 env.trust(USD(1000), alice);
722 env.trust(USD(1000), becky);
723 env.trust(USD(1000), carol);
724 env.close();
725
726 env(pay(gw, alice, USD(1000)));
727 env.close();
728
729 // Make XRP and IOU payments from alice to becky. Should be fine.
730 env(pay(alice, becky, XRP(100)));
731 env(pay(alice, becky, USD(100)));
732 env.close();
733
734 // becky decides to require authorization for deposits.
735 env(fset(becky, asfDepositAuth));
736 env.close();
737
738 // alice can no longer pay becky.
739 env(pay(alice, becky, XRP(100)), ter(tecNO_PERMISSION));
740 env(pay(alice, becky, USD(100)), ter(tecNO_PERMISSION));
741 env.close();
742
743 // becky preauthorizes carol for deposit, which doesn't provide
744 // authorization for alice.
745 env(deposit::auth(becky, carol));
746 env.close();
747
748 // alice still can't pay becky.
749 env(pay(alice, becky, XRP(100)), ter(tecNO_PERMISSION));
750 env(pay(alice, becky, USD(100)), ter(tecNO_PERMISSION));
751 env.close();
752
753 // becky preauthorizes alice for deposit.
754 env(deposit::auth(becky, alice));
755 env.close();
756
757 // alice can now pay becky.
758 env(pay(alice, becky, XRP(100)));
759 env(pay(alice, becky, USD(100)));
760 env.close();
761
762 // alice decides to require authorization for deposits.
763 env(fset(alice, asfDepositAuth));
764 env.close();
765
766 // Even though alice is authorized to pay becky, becky is not
767 // authorized to pay alice.
768 env(pay(becky, alice, XRP(100)), ter(tecNO_PERMISSION));
769 env(pay(becky, alice, USD(100)), ter(tecNO_PERMISSION));
770 env.close();
771
772 // becky unauthorizes carol. Should have no impact on alice.
773 env(deposit::unauth(becky, carol));
774 env.close();
775
776 env(pay(alice, becky, XRP(100)));
777 env(pay(alice, becky, USD(100)));
778 env.close();
779
780 // becky unauthorizes alice. alice now can't pay becky.
781 env(deposit::unauth(becky, alice));
782 env.close();
783
784 env(pay(alice, becky, XRP(100)), ter(tecNO_PERMISSION));
785 env(pay(alice, becky, USD(100)), ter(tecNO_PERMISSION));
786 env.close();
787
788 // becky decides to remove authorization for deposits. Now
789 // alice can pay becky again.
790 env(fclear(becky, asfDepositAuth));
791 env.close();
792
793 env(pay(alice, becky, XRP(100)));
794 env(pay(alice, becky, USD(100)));
795 env.close();
796 }
797 }
798
799 void
801 {
802 using namespace jtx;
803
804 char const credType[] = "abcde";
805 Account const issuer{"issuer"};
806 Account const alice{"alice"};
807 Account const bob{"bob"};
808 Account const maria{"maria"};
809 Account const john{"john"};
810
811 {
812 testcase("Payment failure with disabled credentials rule.");
813
814 Env env(*this, testable_amendments() - featureCredentials);
815
816 env.fund(XRP(5000), issuer, bob, alice);
817 env.close();
818
819 // Bob require preauthorization
820 env(fset(bob, asfDepositAuth));
821 env.close();
822
823 // Setup DepositPreauth object failed - amendent is not supported
824 env(deposit::authCredentials(bob, {{issuer, credType}}),
826 env.close();
827
828 // But can create old DepositPreauth
829 env(deposit::auth(bob, alice));
830 env.close();
831
832 // And alice can't pay with any credentials, amendement is not
833 // enabled
834 std::string const invalidIdx =
835 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
836 "01E034";
837 env(pay(alice, bob, XRP(10)),
838 credentials::ids({invalidIdx}),
840 env.close();
841 }
842
843 {
844 testcase("Payment with credentials.");
845
846 Env env(*this);
847
848 env.fund(XRP(5000), issuer, alice, bob, john);
849 env.close();
850
851 // Issuer create credentials, but Alice didn't accept them yet
852 env(credentials::create(alice, issuer, credType));
853 env.close();
854
855 // Get the index of the credentials
856 auto const jv =
857 credentials::ledgerEntry(env, alice, issuer, credType);
858 std::string const credIdx = jv[jss::result][jss::index].asString();
859
860 // Bob require preauthorization
861 env(fset(bob, asfDepositAuth));
862 env.close();
863
864 // Bob will accept payements from accounts with credentials signed
865 // by 'issuer'
866 env(deposit::authCredentials(bob, {{issuer, credType}}));
867 env.close();
868
869 auto const jDP =
870 ledgerEntryDepositPreauth(env, bob, {{issuer, credType}});
871 BEAST_EXPECT(
872 jDP.isObject() && jDP.isMember(jss::result) &&
873 !jDP[jss::result].isMember(jss::error) &&
874 jDP[jss::result].isMember(jss::node) &&
875 jDP[jss::result][jss::node].isMember("LedgerEntryType") &&
876 jDP[jss::result][jss::node]["LedgerEntryType"] ==
877 jss::DepositPreauth);
878
879 // Alice can't pay - empty credentials array
880 {
881 auto jv = pay(alice, bob, XRP(100));
882 jv[sfCredentialIDs.jsonName] = Json::arrayValue;
883 env(jv, ter(temMALFORMED));
884 env.close();
885 }
886
887 // Alice can't pay - not accepted credentials
888 env(pay(alice, bob, XRP(100)),
889 credentials::ids({credIdx}),
891 env.close();
892
893 // Alice accept the credentials
894 env(credentials::accept(alice, issuer, credType));
895 env.close();
896
897 // Now Alice can pay
898 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}));
899 env.close();
900
901 // Alice can pay Maria without depositPreauth enabled
902 env(pay(alice, maria, XRP(250)), credentials::ids({credIdx}));
903 env.close();
904
905 // john can accept payment with old depositPreauth and valid
906 // credentials
907 env(fset(john, asfDepositAuth));
908 env(deposit::auth(john, alice));
909 env(pay(alice, john, XRP(100)), credentials::ids({credIdx}));
910 env.close();
911 }
912
913 {
914 testcase("Payment failure with invalid credentials.");
915
916 Env env(*this);
917
918 env.fund(XRP(10000), issuer, alice, bob, maria);
919 env.close();
920
921 // Issuer create credentials, but Alice didn't accept them yet
922 env(credentials::create(alice, issuer, credType));
923 env.close();
924 // Alice accept the credentials
925 env(credentials::accept(alice, issuer, credType));
926 env.close();
927 // Get the index of the credentials
928 auto const jv =
929 credentials::ledgerEntry(env, alice, issuer, credType);
930 std::string const credIdx = jv[jss::result][jss::index].asString();
931
932 {
933 // Success as destination didn't enable preauthorization so
934 // valid credentials will not fail
935 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}));
936 }
937
938 // Bob require preauthorization
939 env(fset(bob, asfDepositAuth));
940 env.close();
941
942 {
943 // Fail as destination didn't setup DepositPreauth object
944 env(pay(alice, bob, XRP(100)),
945 credentials::ids({credIdx}),
947 }
948
949 // Bob setup DepositPreauth object, duplicates is not allowed
951 bob, {{issuer, credType}, {issuer, credType}}),
953
954 // Bob setup DepositPreauth object
955 env(deposit::authCredentials(bob, {{issuer, credType}}));
956 env.close();
957
958 {
959 std::string const invalidIdx =
960 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
961 "01E034";
962 // Alice can't pay with non-existing credentials
963 env(pay(alice, bob, XRP(100)),
964 credentials::ids({invalidIdx}),
966 }
967
968 { // maria can't pay using valid credentials but issued for
969 // different account
970 env(pay(maria, bob, XRP(100)),
971 credentials::ids({credIdx}),
973 }
974
975 {
976 // create another valid credential
977 char const credType2[] = "fghij";
978 env(credentials::create(alice, issuer, credType2));
979 env.close();
980 env(credentials::accept(alice, issuer, credType2));
981 env.close();
982 auto const jv =
983 credentials::ledgerEntry(env, alice, issuer, credType2);
984 std::string const credIdx2 =
985 jv[jss::result][jss::index].asString();
986
987 // Alice can't pay with invalid set of valid credentials
988 env(pay(alice, bob, XRP(100)),
989 credentials::ids({credIdx, credIdx2}),
991 }
992
993 // Error, duplicate credentials
994 env(pay(alice, bob, XRP(100)),
995 credentials::ids({credIdx, credIdx}),
997
998 // Alice can pay
999 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}));
1000 env.close();
1001 }
1002 }
1003
1004 void
1006 {
1007 using namespace jtx;
1008
1009 char const credType[] = "abcde";
1010 Account const issuer{"issuer"};
1011 Account const alice{"alice"};
1012 Account const bob{"bob"};
1013 Account const maria{"maria"};
1014
1015 {
1016 testcase("Creating / deleting with credentials.");
1017
1018 Env env(*this);
1019
1020 env.fund(XRP(5000), issuer, alice, bob);
1021 env.close();
1022
1023 {
1024 // both included [AuthorizeCredentials UnauthorizeCredentials]
1025 auto jv = deposit::authCredentials(bob, {{issuer, credType}});
1026 jv[sfUnauthorizeCredentials.jsonName] = Json::arrayValue;
1027 env(jv, ter(temMALFORMED));
1028 }
1029
1030 {
1031 // both included [Unauthorize, AuthorizeCredentials]
1032 auto jv = deposit::authCredentials(bob, {{issuer, credType}});
1033 jv[sfUnauthorize.jsonName] = issuer.human();
1034 env(jv, ter(temMALFORMED));
1035 }
1036
1037 {
1038 // both included [Authorize, AuthorizeCredentials]
1039 auto jv = deposit::authCredentials(bob, {{issuer, credType}});
1040 jv[sfAuthorize.jsonName] = issuer.human();
1041 env(jv, ter(temMALFORMED));
1042 }
1043
1044 {
1045 // both included [Unauthorize, UnauthorizeCredentials]
1046 auto jv = deposit::unauthCredentials(bob, {{issuer, credType}});
1047 jv[sfUnauthorize.jsonName] = issuer.human();
1048 env(jv, ter(temMALFORMED));
1049 }
1050
1051 {
1052 // both included [Authorize, UnauthorizeCredentials]
1053 auto jv = deposit::unauthCredentials(bob, {{issuer, credType}});
1054 jv[sfAuthorize.jsonName] = issuer.human();
1055 env(jv, ter(temMALFORMED));
1056 }
1057
1058 {
1059 // AuthorizeCredentials is empty
1060 auto jv = deposit::authCredentials(bob, {});
1061 env(jv, ter(temARRAY_EMPTY));
1062 }
1063
1064 {
1065 // invalid issuer
1066 auto jv = deposit::authCredentials(bob, {});
1067 auto& arr(jv[sfAuthorizeCredentials.jsonName]);
1069 cred[jss::Issuer] = to_string(xrpAccount());
1070 cred[sfCredentialType.jsonName] =
1071 strHex(std::string_view(credType));
1072 Json::Value credParent;
1073 credParent[jss::Credential] = cred;
1074 arr.append(std::move(credParent));
1075
1076 env(jv, ter(temINVALID_ACCOUNT_ID));
1077 }
1078
1079 {
1080 // empty credential type
1081 auto jv = deposit::authCredentials(bob, {{issuer, {}}});
1082 env(jv, ter(temMALFORMED));
1083 }
1084
1085 {
1086 // AuthorizeCredentials is larger than 8 elements
1087 Account const a("a"), b("b"), c("c"), d("d"), e("e"), f("f"),
1088 g("g"), h("h"), i("i");
1089 auto const& z = credType;
1090 auto jv = deposit::authCredentials(
1091 bob,
1092 {{a, z},
1093 {b, z},
1094 {c, z},
1095 {d, z},
1096 {e, z},
1097 {f, z},
1098 {g, z},
1099 {h, z},
1100 {i, z}});
1101 env(jv, ter(temARRAY_TOO_LARGE));
1102 }
1103
1104 {
1105 // Can't create with non-existing issuer
1106 Account const rick{"rick"};
1107 auto jv = deposit::authCredentials(bob, {{rick, credType}});
1108 env(jv, ter(tecNO_ISSUER));
1109 env.close();
1110 }
1111
1112 {
1113 // not enough reserve
1114 Account const john{"john"};
1115 env.fund(env.current()->fees().accountReserve(0), john);
1116 env.close();
1117 auto jv = deposit::authCredentials(john, {{issuer, credType}});
1118 env(jv, ter(tecINSUFFICIENT_RESERVE));
1119 }
1120
1121 {
1122 // NO deposit object exists
1123 env(deposit::unauthCredentials(bob, {{issuer, credType}}),
1124 ter(tecNO_ENTRY));
1125 }
1126
1127 // Create DepositPreauth object
1128 {
1129 env(deposit::authCredentials(bob, {{issuer, credType}}));
1130 env.close();
1131
1132 auto const jDP =
1133 ledgerEntryDepositPreauth(env, bob, {{issuer, credType}});
1134 BEAST_EXPECT(
1135 jDP.isObject() && jDP.isMember(jss::result) &&
1136 !jDP[jss::result].isMember(jss::error) &&
1137 jDP[jss::result].isMember(jss::node) &&
1138 jDP[jss::result][jss::node].isMember("LedgerEntryType") &&
1139 jDP[jss::result][jss::node]["LedgerEntryType"] ==
1140 jss::DepositPreauth);
1141
1142 // Check object fields
1143 BEAST_EXPECT(
1144 jDP[jss::result][jss::node][jss::Account] == bob.human());
1145 auto const& credentials(
1146 jDP[jss::result][jss::node]["AuthorizeCredentials"]);
1147 BEAST_EXPECT(credentials.isArray() && credentials.size() == 1);
1148 for (auto const& o : credentials)
1149 {
1150 auto const& c(o[jss::Credential]);
1151 BEAST_EXPECT(c[jss::Issuer].asString() == issuer.human());
1152 BEAST_EXPECT(
1153 c["CredentialType"].asString() ==
1154 strHex(std::string_view(credType)));
1155 }
1156
1157 // can't create duplicate
1158 env(deposit::authCredentials(bob, {{issuer, credType}}),
1159 ter(tecDUPLICATE));
1160 }
1161
1162 // Delete DepositPreauth object
1163 {
1164 env(deposit::unauthCredentials(bob, {{issuer, credType}}));
1165 env.close();
1166 auto const jDP =
1167 ledgerEntryDepositPreauth(env, bob, {{issuer, credType}});
1168 BEAST_EXPECT(
1169 jDP.isObject() && jDP.isMember(jss::result) &&
1170 jDP[jss::result].isMember(jss::error) &&
1171 jDP[jss::result][jss::error] == "entryNotFound");
1172 }
1173 }
1174 }
1175
1176 void
1178 {
1179 using namespace jtx;
1180 char const credType[] = "abcde";
1181 char const credType2[] = "fghijkl";
1182 Account const issuer{"issuer"};
1183 Account const alice{"alice"};
1184 Account const bob{"bob"};
1185 Account const gw{"gw"};
1186 IOU const USD = gw["USD"];
1187 Account const zelda{"zelda"};
1188
1189 {
1190 testcase("Payment failure with expired credentials.");
1191
1192 Env env(*this);
1193
1194 env.fund(XRP(10000), issuer, alice, bob, gw);
1195 env.close();
1196
1197 // Create credentials
1198 auto jv = credentials::create(alice, issuer, credType);
1199 // Current time in ripple epoch.
1200 // Every time ledger close, unittest timer increase by 10s
1201 uint32_t const t = env.current()
1202 ->info()
1203 .parentCloseTime.time_since_epoch()
1204 .count() +
1205 60;
1206 jv[sfExpiration.jsonName] = t;
1207 env(jv);
1208 env.close();
1209
1210 // Alice accept the credentials
1211 env(credentials::accept(alice, issuer, credType));
1212 env.close();
1213
1214 // Create credential which not expired
1215 jv = credentials::create(alice, issuer, credType2);
1216 uint32_t const t2 = env.current()
1217 ->info()
1218 .parentCloseTime.time_since_epoch()
1219 .count() +
1220 1000;
1221 jv[sfExpiration.jsonName] = t2;
1222 env(jv);
1223 env.close();
1224 env(credentials::accept(alice, issuer, credType2));
1225 env.close();
1226
1227 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1228 BEAST_EXPECT(ownerCount(env, alice) == 2);
1229
1230 // Get the index of the credentials
1231 jv = credentials::ledgerEntry(env, alice, issuer, credType);
1232 std::string const credIdx = jv[jss::result][jss::index].asString();
1233 jv = credentials::ledgerEntry(env, alice, issuer, credType2);
1234 std::string const credIdx2 = jv[jss::result][jss::index].asString();
1235
1236 // Bob require preauthorization
1237 env(fset(bob, asfDepositAuth));
1238 env.close();
1239 // Bob setup DepositPreauth object
1241 bob, {{issuer, credType}, {issuer, credType2}}));
1242 env.close();
1243
1244 {
1245 // Alice can pay
1246 env(pay(alice, bob, XRP(100)),
1247 credentials::ids({credIdx, credIdx2}));
1248 env.close();
1249 env.close();
1250
1251 // Ledger closed, time increased, alice can't pay anymore
1252 env(pay(alice, bob, XRP(100)),
1253 credentials::ids({credIdx, credIdx2}),
1254 ter(tecEXPIRED));
1255 env.close();
1256
1257 {
1258 // check that expired credentials were deleted
1259 auto const jDelCred =
1260 credentials::ledgerEntry(env, alice, issuer, credType);
1261 BEAST_EXPECT(
1262 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1263 jDelCred[jss::result].isMember(jss::error) &&
1264 jDelCred[jss::result][jss::error] == "entryNotFound");
1265 }
1266
1267 {
1268 // check that non-expired credential still present
1269 auto const jle =
1270 credentials::ledgerEntry(env, alice, issuer, credType2);
1271 BEAST_EXPECT(
1272 jle.isObject() && jle.isMember(jss::result) &&
1273 !jle[jss::result].isMember(jss::error) &&
1274 jle[jss::result].isMember(jss::node) &&
1275 jle[jss::result][jss::node].isMember(
1276 "LedgerEntryType") &&
1277 jle[jss::result][jss::node]["LedgerEntryType"] ==
1278 jss::Credential &&
1279 jle[jss::result][jss::node][jss::Issuer] ==
1280 issuer.human() &&
1281 jle[jss::result][jss::node][jss::Subject] ==
1282 alice.human() &&
1283 jle[jss::result][jss::node]["CredentialType"] ==
1284 strHex(std::string_view(credType2)));
1285 }
1286
1287 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1288 BEAST_EXPECT(ownerCount(env, alice) == 1);
1289 }
1290
1291 {
1292 auto jv = credentials::create(gw, issuer, credType);
1293 uint32_t const t = env.current()
1294 ->info()
1295 .parentCloseTime.time_since_epoch()
1296 .count() +
1297 40;
1298 jv[sfExpiration.jsonName] = t;
1299 env(jv);
1300 env.close();
1301 env(credentials::accept(gw, issuer, credType));
1302 env.close();
1303
1304 jv = credentials::ledgerEntry(env, gw, issuer, credType);
1305 std::string const credIdx =
1306 jv[jss::result][jss::index].asString();
1307
1308 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1309 BEAST_EXPECT(ownerCount(env, gw) == 1);
1310
1311 env.close();
1312 env.close();
1313 env.close();
1314
1315 // credentials are expired
1316 env(pay(gw, bob, USD(150)),
1317 credentials::ids({credIdx}),
1318 ter(tecEXPIRED));
1319 env.close();
1320
1321 // check that expired credentials were deleted
1322 auto const jDelCred =
1323 credentials::ledgerEntry(env, gw, issuer, credType);
1324 BEAST_EXPECT(
1325 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1326 jDelCred[jss::result].isMember(jss::error) &&
1327 jDelCred[jss::result][jss::error] == "entryNotFound");
1328
1329 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1330 BEAST_EXPECT(ownerCount(env, gw) == 0);
1331 }
1332 }
1333
1334 {
1335 using namespace std::chrono;
1336
1337 testcase("Escrow failure with expired credentials.");
1338
1339 Env env(*this);
1340
1341 env.fund(XRP(5000), issuer, alice, bob, zelda);
1342 env.close();
1343
1344 // Create credentials
1345 auto jv = credentials::create(zelda, issuer, credType);
1346 uint32_t const t = env.current()
1347 ->info()
1348 .parentCloseTime.time_since_epoch()
1349 .count() +
1350 50;
1351 jv[sfExpiration.jsonName] = t;
1352 env(jv);
1353 env.close();
1354
1355 // Zelda accept the credentials
1356 env(credentials::accept(zelda, issuer, credType));
1357 env.close();
1358
1359 // Get the index of the credentials
1360 jv = credentials::ledgerEntry(env, zelda, issuer, credType);
1361 std::string const credIdx = jv[jss::result][jss::index].asString();
1362
1363 // Bob require preauthorization
1364 env(fset(bob, asfDepositAuth));
1365 env.close();
1366 // Bob setup DepositPreauth object
1367 env(deposit::authCredentials(bob, {{issuer, credType}}));
1368 env.close();
1369
1370 auto const seq = env.seq(alice);
1371 env(escrow::create(alice, bob, XRP(1000)),
1372 escrow::finish_time(env.now() + 1s));
1373 env.close();
1374
1375 // zelda can't finish escrow with invalid credentials
1376 {
1377 env(escrow::finish(zelda, alice, seq),
1378 credentials::ids({}),
1379 ter(temMALFORMED));
1380 env.close();
1381 }
1382
1383 {
1384 // zelda can't finish escrow with invalid credentials
1385 std::string const invalidIdx =
1386 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
1387 "01E034";
1388
1389 env(escrow::finish(zelda, alice, seq),
1390 credentials::ids({invalidIdx}),
1392 env.close();
1393 }
1394
1395 { // Ledger closed, time increased, zelda can't finish escrow
1396 env(escrow::finish(zelda, alice, seq),
1397 credentials::ids({credIdx}),
1398 fee(1500),
1399 ter(tecEXPIRED));
1400 env.close();
1401 }
1402
1403 // check that expired credentials were deleted
1404 auto const jDelCred =
1405 credentials::ledgerEntry(env, zelda, issuer, credType);
1406 BEAST_EXPECT(
1407 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1408 jDelCred[jss::result].isMember(jss::error) &&
1409 jDelCred[jss::result][jss::error] == "entryNotFound");
1410 }
1411 }
1412
1413 void
1415 {
1416 using namespace jtx;
1417
1418 Account const stock{"stock"};
1419 Account const alice{"alice"};
1420 Account const bob{"bob"};
1421
1422 Env env(*this);
1423
1424 testcase("Sorting credentials.");
1425
1426 env.fund(XRP(5000), stock, alice, bob);
1427
1429 {"a", "a"},
1430 {"b", "b"},
1431 {"c", "c"},
1432 {"d", "d"},
1433 {"e", "e"},
1434 {"f", "f"},
1435 {"g", "g"},
1436 {"h", "h"}};
1437
1438 for (auto const& c : credentials)
1439 env.fund(XRP(5000), c.issuer);
1440 env.close();
1441
1443 std::mt19937 gen(rd());
1444
1445 {
1447 for (auto const& c : credentials)
1448 pubKey2Acc.emplace(c.issuer.human(), c.issuer);
1449
1450 // check sorting in object
1451 for (int i = 0; i < 10; ++i)
1452 {
1453 std::ranges::shuffle(credentials, gen);
1454 env(deposit::authCredentials(stock, credentials));
1455 env.close();
1456
1457 auto const dp =
1458 ledgerEntryDepositPreauth(env, stock, credentials);
1459 auto const& authCred(
1460 dp[jss::result][jss::node]["AuthorizeCredentials"]);
1461 BEAST_EXPECT(
1462 authCred.isArray() &&
1463 authCred.size() == credentials.size());
1465 for (auto const& o : authCred)
1466 {
1467 auto const& c(o[jss::Credential]);
1468 auto issuer = c[jss::Issuer].asString();
1469
1470 if (BEAST_EXPECT(pubKey2Acc.contains(issuer)))
1471 readedCreds.emplace_back(
1472 pubKey2Acc.at(issuer),
1473 c["CredentialType"].asString());
1474 }
1475
1476 BEAST_EXPECT(std::ranges::is_sorted(readedCreds));
1477
1478 env(deposit::unauthCredentials(stock, credentials));
1479 env.close();
1480 }
1481 }
1482
1483 {
1484 std::ranges::shuffle(credentials, gen);
1485 env(deposit::authCredentials(stock, credentials));
1486 env.close();
1487
1488 // check sorting in params
1489 for (int i = 0; i < 10; ++i)
1490 {
1491 std::ranges::shuffle(credentials, gen);
1492 env(deposit::authCredentials(stock, credentials),
1493 ter(tecDUPLICATE));
1494 }
1495 }
1496
1497 testcase("Check duplicate credentials.");
1498 {
1499 // check duplicates in depositPreauth params
1501 credentials.begin(), credentials.end() - 1);
1502
1503 std::ranges::shuffle(copyCredentials, gen);
1504 for (auto const& c : copyCredentials)
1505 {
1506 auto credentials2 = copyCredentials;
1507 credentials2.push_back(c);
1508 env(deposit::authCredentials(stock, credentials2),
1509 ter(temMALFORMED));
1510 }
1511
1512 // create batch of credentials and save their hashes
1513 std::vector<std::string> credentialIDs;
1514 for (auto const& c : credentials)
1515 {
1516 env(credentials::create(alice, c.issuer, c.credType));
1517 env.close();
1518 env(credentials::accept(alice, c.issuer, c.credType));
1519 env.close();
1520
1521 credentialIDs.push_back(credentials::ledgerEntry(
1522 env,
1523 alice,
1524 c.issuer,
1525 c.credType)[jss::result][jss::index]
1526 .asString());
1527 }
1528
1529 // check duplicates in payment params
1530 for (auto const& h : credentialIDs)
1531 {
1532 auto credentialIDs2 = credentialIDs;
1533 credentialIDs2.push_back(h);
1534
1535 env(pay(alice, bob, XRP(100)),
1536 credentials::ids(credentialIDs2),
1537 ter(temMALFORMED));
1538 }
1539 }
1540 }
1541
1542 void
1543 run() override
1544 {
1545 testEnable();
1546 testInvalid();
1547 auto const supported{jtx::testable_amendments()};
1548 testPayment(supported - featureDepositPreauth - featureCredentials);
1549 testPayment(supported - featureDepositPreauth);
1550 testPayment(supported - featureCredentials);
1551 testPayment(supported);
1556 }
1557};
1558
1559BEAST_DEFINE_TESTSUITE(DepositAuth, app, ripple);
1560BEAST_DEFINE_TESTSUITE(DepositPreauth, app, ripple);
1561
1562} // namespace test
1563} // namespace ripple
T at(T... args)
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
Value & append(Value const &value)
Append value to array at the end.
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition suite.h:226
Immutable cryptographic account descriptor.
Definition Account.h:20
std::string const & human() const
Returns the human readable public key.
Definition Account.h:99
A transaction testing environment.
Definition Env.h:102
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:250
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:528
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:312
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:103
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:302
NetClock::time_point now()
Returns the current network time.
Definition Env.h:265
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:772
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:271
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:165
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:138
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:259
Converts to IOU Issue or STAmount.
Set the fee on a JTx.
Definition fee.h:18
Match set account flags.
Definition flags.h:109
Match clear account flags.
Definition flags.h:126
Match the number of items in the account's owner directory.
Definition owners.h:54
Add a path.
Definition paths.h:39
Check a set of conditions.
Definition require.h:47
Sets the SendMax on a JTx.
Definition sendmax.h:14
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:16
Set a ticket sequence on a JTx.
Definition ticket.h:29
Set the flags on a JTx.
Definition txflags.h:12
T contains(T... args)
T emplace_back(T... args)
T emplace(T... args)
T end(T... args)
T is_sorted(T... args)
@ arrayValue
array value (ordered list)
Definition json_value.h:25
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:13
Json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:29
Json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:59
Json::Value unauth(Account const &account, Account const &unauth)
Remove preauthorization for deposit.
Definition deposit.cpp:24
Json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition deposit.cpp:13
Json::Value unauthCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:55
Json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:35
Json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount)
Definition escrow.cpp:14
auto const finish_time
Set the "FinishAfter" time tag on a JTx.
Definition escrow.h:79
Json::Value finish(AccountID const &account, AccountID const &from, std::uint32_t seq)
Definition escrow.cpp:26
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
std::uint32_t ownerCount(Env const &env, Account const &account)
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:102
owner_count< ltOFFER > offers
Match the number of offers in the account's owner directory.
Definition owners.h:73
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
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 pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:55
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
owner_count< ltTICKET > tickets
Match the number of tickets on the account.
Definition ticket.h:45
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
static Json::Value ledgerEntryDepositPreauth(jtx::Env &env, jtx::Account const &acc, std::vector< jtx::deposit::AuthorizeCredentials > const &auth)
static bool hasDepositAuth(jtx::Env const &env, jtx::Account const &acct)
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:6
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:66
AccountID const & xrpAccount()
Compute AccountID from public key.
constexpr std::uint32_t tfPassive
Definition TxFlags.h:79
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
@ tecNO_ENTRY
Definition TER.h:288
@ tecNO_ISSUER
Definition TER.h:281
@ tecNO_TARGET
Definition TER.h:286
@ tecDUPLICATE
Definition TER.h:297
@ tecBAD_CREDENTIALS
Definition TER.h:341
@ tecNO_PERMISSION
Definition TER.h:287
@ tecPATH_DRY
Definition TER.h:276
@ tecINSUFFICIENT_RESERVE
Definition TER.h:289
@ tecEXPIRED
Definition TER.h:296
@ tesSUCCESS
Definition TER.h:226
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
constexpr std::uint32_t tfSell
Definition TxFlags.h:82
@ terINSUF_FEE_B
Definition TER.h:197
@ terNO_ACCOUNT
Definition TER.h:198
TERSubset< CanCvtToTER > TER
Definition TER.h:630
constexpr std::uint32_t tfSetNoRipple
Definition TxFlags.h:97
@ temBAD_FEE
Definition TER.h:73
@ temMALFORMED
Definition TER.h:68
@ temINVALID_FLAG
Definition TER.h:92
@ temARRAY_EMPTY
Definition TER.h:121
@ temARRAY_TOO_LARGE
Definition TER.h:122
@ temDISABLED
Definition TER.h:95
@ temCANNOT_PREAUTH_SELF
Definition TER.h:101
@ temINVALID_ACCOUNT_ID
Definition TER.h:100
T push_back(T... args)
T shuffle(T... args)
T size(T... args)
void run() override
Runs the suite.
void testPayment(FeatureBitset features)
void run() override
Runs the suite.
Represents an XRP or IOU quantity This customizes the string conversion and supports XRP conversions ...
Set the sequence number on a JTx.
Definition seq.h:15