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