rippled
Loading...
Searching...
No Matches
Credentials_test.cpp
1#include <test/jtx.h>
2
3#include <xrpl/basics/strHex.h>
4#include <xrpl/ledger/ApplyViewImpl.h>
5#include <xrpl/ledger/CredentialHelpers.h>
6#include <xrpl/protocol/Feature.h>
7#include <xrpl/protocol/Indexes.h>
8#include <xrpl/protocol/Protocol.h>
9#include <xrpl/protocol/TxFlags.h>
10#include <xrpl/protocol/jss.h>
11
12#include <string_view>
13
14namespace xrpl {
15namespace test {
16
18{
19 void
21 {
22 using namespace test::jtx;
23
24 char const credType[] = "abcde";
25 char const uri[] = "uri";
26
27 Account const issuer{"issuer"};
28 Account const subject{"subject"};
29 Account const other{"other"};
30
31 Env env{*this, features};
32
33 {
34 testcase("Create for subject.");
35
36 auto const credKey = credentials::keylet(subject, issuer, credType);
37
38 env.fund(XRP(5000), subject, issuer, other);
39 env.close();
40
41 // Test Create credentials
42 env(credentials::create(subject, issuer, credType),
43 credentials::uri(uri));
44 env.close();
45 {
46 auto const sleCred = env.le(credKey);
47 BEAST_EXPECT(static_cast<bool>(sleCred));
48 if (!sleCred)
49 return;
50
51 BEAST_EXPECT(sleCred->getAccountID(sfSubject) == subject.id());
52 BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id());
53 BEAST_EXPECT(!sleCred->getFieldU32(sfFlags));
54 BEAST_EXPECT(ownerCount(env, issuer) == 1);
55 BEAST_EXPECT(!ownerCount(env, subject));
56 BEAST_EXPECT(checkVL(sleCred, sfCredentialType, credType));
57 BEAST_EXPECT(checkVL(sleCred, sfURI, uri));
58 auto const jle =
59 credentials::ledgerEntry(env, subject, issuer, credType);
60 BEAST_EXPECT(
61 jle.isObject() && jle.isMember(jss::result) &&
62 !jle[jss::result].isMember(jss::error) &&
63 jle[jss::result].isMember(jss::node) &&
64 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
65 jle[jss::result][jss::node]["LedgerEntryType"] ==
66 jss::Credential &&
67 jle[jss::result][jss::node][jss::Issuer] ==
68 issuer.human() &&
69 jle[jss::result][jss::node][jss::Subject] ==
70 subject.human() &&
71 jle[jss::result][jss::node]["CredentialType"] ==
72 strHex(std::string_view(credType)));
73 }
74
75 env(credentials::accept(subject, issuer, credType));
76 env.close();
77 {
78 // check switching owner of the credentials from issuer to
79 // subject
80 auto const sleCred = env.le(credKey);
81 BEAST_EXPECT(static_cast<bool>(sleCred));
82 if (!sleCred)
83 return;
84
85 BEAST_EXPECT(sleCred->getAccountID(sfSubject) == subject.id());
86 BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id());
87 BEAST_EXPECT(!ownerCount(env, issuer));
88 BEAST_EXPECT(ownerCount(env, subject) == 1);
89 BEAST_EXPECT(checkVL(sleCred, sfCredentialType, credType));
90 BEAST_EXPECT(checkVL(sleCred, sfURI, uri));
91 BEAST_EXPECT(sleCred->getFieldU32(sfFlags) == lsfAccepted);
92 }
93
94 env(credentials::deleteCred(subject, subject, issuer, credType));
95 env.close();
96 {
97 BEAST_EXPECT(!env.le(credKey));
98 BEAST_EXPECT(!ownerCount(env, issuer));
99 BEAST_EXPECT(!ownerCount(env, subject));
100
101 // check no credential exists anymore
102 auto const jle =
103 credentials::ledgerEntry(env, subject, issuer, credType);
104 BEAST_EXPECT(
105 jle.isObject() && jle.isMember(jss::result) &&
106 jle[jss::result].isMember(jss::error) &&
107 jle[jss::result][jss::error] == "entryNotFound");
108 }
109 }
110
111 {
112 testcase("Create for themself.");
113
114 auto const credKey = credentials::keylet(issuer, issuer, credType);
115
116 env(credentials::create(issuer, issuer, credType),
117 credentials::uri(uri));
118 env.close();
119 {
120 auto const sleCred = env.le(credKey);
121 BEAST_EXPECT(static_cast<bool>(sleCred));
122 if (!sleCred)
123 return;
124
125 BEAST_EXPECT(sleCred->getAccountID(sfSubject) == issuer.id());
126 BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id());
127 BEAST_EXPECT((sleCred->getFieldU32(sfFlags) & lsfAccepted));
128 BEAST_EXPECT(
129 sleCred->getFieldU64(sfIssuerNode) ==
130 sleCred->getFieldU64(sfSubjectNode));
131 BEAST_EXPECT(ownerCount(env, issuer) == 1);
132 BEAST_EXPECT(checkVL(sleCred, sfCredentialType, credType));
133 BEAST_EXPECT(checkVL(sleCred, sfURI, uri));
134 auto const jle =
135 credentials::ledgerEntry(env, issuer, issuer, credType);
136 BEAST_EXPECT(
137 jle.isObject() && jle.isMember(jss::result) &&
138 !jle[jss::result].isMember(jss::error) &&
139 jle[jss::result].isMember(jss::node) &&
140 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
141 jle[jss::result][jss::node]["LedgerEntryType"] ==
142 jss::Credential &&
143 jle[jss::result][jss::node][jss::Issuer] ==
144 issuer.human() &&
145 jle[jss::result][jss::node][jss::Subject] ==
146 issuer.human() &&
147 jle[jss::result][jss::node]["CredentialType"] ==
148 strHex(std::string_view(credType)));
149 }
150
151 env(credentials::deleteCred(issuer, issuer, issuer, credType));
152 env.close();
153 {
154 BEAST_EXPECT(!env.le(credKey));
155 BEAST_EXPECT(!ownerCount(env, issuer));
156
157 // check no credential exists anymore
158 auto const jle =
159 credentials::ledgerEntry(env, issuer, issuer, credType);
160 BEAST_EXPECT(
161 jle.isObject() && jle.isMember(jss::result) &&
162 jle[jss::result].isMember(jss::error) &&
163 jle[jss::result][jss::error] == "entryNotFound");
164 }
165 }
166 }
167
168 void
170 {
171 using namespace test::jtx;
172
173 char const credType[] = "abcde";
174
175 Account const issuer{"issuer"};
176 Account const subject{"subject"};
177 Account const other{"other"};
178
179 Env env{*this, features};
180
181 // fund subject and issuer
182 env.fund(XRP(5000), issuer, subject, other);
183 env.close();
184
185 {
186 testcase("Delete issuer before accept");
187
188 auto const credKey = credentials::keylet(subject, issuer, credType);
189 env(credentials::create(subject, issuer, credType));
190 env.close();
191
192 // delete issuer
193 {
194 int const delta = env.seq(issuer) + 255;
195 for (int i = 0; i < delta; ++i)
196 env.close();
197 auto const acctDelFee{drops(env.current()->fees().increment)};
198 env(acctdelete(issuer, other), fee(acctDelFee));
199 env.close();
200 }
201
202 // check credentials deleted too
203 {
204 BEAST_EXPECT(!env.le(credKey));
205 BEAST_EXPECT(!ownerCount(env, subject));
206
207 // check no credential exists anymore
208 auto const jle =
209 credentials::ledgerEntry(env, subject, issuer, credType);
210 BEAST_EXPECT(
211 jle.isObject() && jle.isMember(jss::result) &&
212 jle[jss::result].isMember(jss::error) &&
213 jle[jss::result][jss::error] == "entryNotFound");
214 }
215
216 // resurrection
217 env.fund(XRP(5000), issuer);
218 env.close();
219 }
220
221 {
222 testcase("Delete issuer after accept");
223
224 auto const credKey = credentials::keylet(subject, issuer, credType);
225 env(credentials::create(subject, issuer, credType));
226 env.close();
227 env(credentials::accept(subject, issuer, credType));
228 env.close();
229
230 // delete issuer
231 {
232 int const delta = env.seq(issuer) + 255;
233 for (int i = 0; i < delta; ++i)
234 env.close();
235 auto const acctDelFee{drops(env.current()->fees().increment)};
236 env(acctdelete(issuer, other), fee(acctDelFee));
237 env.close();
238 }
239
240 // check credentials deleted too
241 {
242 BEAST_EXPECT(!env.le(credKey));
243 BEAST_EXPECT(!ownerCount(env, subject));
244
245 // check no credential exists anymore
246 auto const jle =
247 credentials::ledgerEntry(env, subject, issuer, credType);
248 BEAST_EXPECT(
249 jle.isObject() && jle.isMember(jss::result) &&
250 jle[jss::result].isMember(jss::error) &&
251 jle[jss::result][jss::error] == "entryNotFound");
252 }
253
254 // resurrection
255 env.fund(XRP(5000), issuer);
256 env.close();
257 }
258
259 {
260 testcase("Delete subject before accept");
261
262 auto const credKey = credentials::keylet(subject, issuer, credType);
263 env(credentials::create(subject, issuer, credType));
264 env.close();
265
266 // delete subject
267 {
268 int const delta = env.seq(subject) + 255;
269 for (int i = 0; i < delta; ++i)
270 env.close();
271 auto const acctDelFee{drops(env.current()->fees().increment)};
272 env(acctdelete(subject, other), fee(acctDelFee));
273 env.close();
274 }
275
276 // check credentials deleted too
277 {
278 BEAST_EXPECT(!env.le(credKey));
279 BEAST_EXPECT(!ownerCount(env, issuer));
280
281 // check no credential exists anymore
282 auto const jle =
283 credentials::ledgerEntry(env, subject, issuer, credType);
284 BEAST_EXPECT(
285 jle.isObject() && jle.isMember(jss::result) &&
286 jle[jss::result].isMember(jss::error) &&
287 jle[jss::result][jss::error] == "entryNotFound");
288 }
289
290 // resurrection
291 env.fund(XRP(5000), subject);
292 env.close();
293 }
294
295 {
296 testcase("Delete subject after accept");
297
298 auto const credKey = credentials::keylet(subject, issuer, credType);
299 env(credentials::create(subject, issuer, credType));
300 env.close();
301 env(credentials::accept(subject, issuer, credType));
302 env.close();
303
304 // delete subject
305 {
306 int const delta = env.seq(subject) + 255;
307 for (int i = 0; i < delta; ++i)
308 env.close();
309 auto const acctDelFee{drops(env.current()->fees().increment)};
310 env(acctdelete(subject, other), fee(acctDelFee));
311 env.close();
312 }
313
314 // check credentials deleted too
315 {
316 BEAST_EXPECT(!env.le(credKey));
317 BEAST_EXPECT(!ownerCount(env, issuer));
318
319 // check no credential exists anymore
320 auto const jle =
321 credentials::ledgerEntry(env, subject, issuer, credType);
322 BEAST_EXPECT(
323 jle.isObject() && jle.isMember(jss::result) &&
324 jle[jss::result].isMember(jss::error) &&
325 jle[jss::result][jss::error] == "entryNotFound");
326 }
327
328 // resurrection
329 env.fund(XRP(5000), subject);
330 env.close();
331 }
332
333 {
334 testcase("Delete by other");
335
336 auto const credKey = credentials::keylet(subject, issuer, credType);
337 auto jv = credentials::create(subject, issuer, credType);
338 uint32_t const t = env.current()
339 ->header()
340 .parentCloseTime.time_since_epoch()
341 .count();
342 jv[sfExpiration.jsonName] = t + 20;
343 env(jv);
344
345 // time advance
346 env.close();
347 env.close();
348 env.close();
349
350 // Other account delete credentials
351 env(credentials::deleteCred(other, subject, issuer, credType));
352 env.close();
353
354 // check credentials object
355 {
356 BEAST_EXPECT(!env.le(credKey));
357 BEAST_EXPECT(!ownerCount(env, issuer));
358 BEAST_EXPECT(!ownerCount(env, subject));
359
360 // check no credential exists anymore
361 auto const jle =
362 credentials::ledgerEntry(env, subject, issuer, credType);
363 BEAST_EXPECT(
364 jle.isObject() && jle.isMember(jss::result) &&
365 jle[jss::result].isMember(jss::error) &&
366 jle[jss::result][jss::error] == "entryNotFound");
367 }
368 }
369
370 {
371 testcase("Delete by subject");
372
373 env(credentials::create(subject, issuer, credType));
374 env.close();
375
376 // Subject can delete
377 env(credentials::deleteCred(subject, subject, issuer, credType));
378 env.close();
379 {
380 auto const credKey =
381 credentials::keylet(subject, issuer, credType);
382 BEAST_EXPECT(!env.le(credKey));
383 BEAST_EXPECT(!ownerCount(env, subject));
384 BEAST_EXPECT(!ownerCount(env, issuer));
385 auto const jle =
386 credentials::ledgerEntry(env, subject, issuer, credType);
387 BEAST_EXPECT(
388 jle.isObject() && jle.isMember(jss::result) &&
389 jle[jss::result].isMember(jss::error) &&
390 jle[jss::result][jss::error] == "entryNotFound");
391 }
392 }
393
394 {
395 testcase("Delete by issuer");
396 env(credentials::create(subject, issuer, credType));
397 env.close();
398
399 env(credentials::deleteCred(issuer, subject, issuer, credType));
400 env.close();
401 {
402 auto const credKey =
403 credentials::keylet(subject, issuer, credType);
404 BEAST_EXPECT(!env.le(credKey));
405 BEAST_EXPECT(!ownerCount(env, subject));
406 BEAST_EXPECT(!ownerCount(env, issuer));
407 auto const jle =
408 credentials::ledgerEntry(env, subject, issuer, credType);
409 BEAST_EXPECT(
410 jle.isObject() && jle.isMember(jss::result) &&
411 jle[jss::result].isMember(jss::error) &&
412 jle[jss::result][jss::error] == "entryNotFound");
413 }
414 }
415 }
416
417 void
419 {
420 using namespace test::jtx;
421
422 char const credType[] = "abcde";
423
424 Account const issuer{"issuer"};
425 Account const subject{"subject"};
426
427 {
428 using namespace jtx;
429 Env env{*this, features};
430
431 env.fund(XRP(5000), subject, issuer);
432 env.close();
433
434 {
435 testcase("Credentials fail, no subject param.");
436 auto jv = credentials::create(subject, issuer, credType);
437 jv.removeMember(jss::Subject);
438 env(jv, ter(temMALFORMED));
439 }
440
441 {
442 auto jv = credentials::create(subject, issuer, credType);
443 jv[jss::Subject] = to_string(xrpAccount());
444 env(jv, ter(temMALFORMED));
445 }
446
447 {
448 testcase("Credentials fail, no credentialType param.");
449 auto jv = credentials::create(subject, issuer, credType);
450 jv.removeMember(sfCredentialType.jsonName);
451 env(jv, ter(temMALFORMED));
452 }
453
454 {
455 testcase("Credentials fail, empty credentialType param.");
456 auto jv = credentials::create(subject, issuer, "");
457 env(jv, ter(temMALFORMED));
458 }
459
460 {
461 testcase(
462 "Credentials fail, credentialType length > "
463 "maxCredentialTypeLength.");
464 constexpr std::string_view longCredType =
465 "abcdefghijklmnopqrstuvwxyz01234567890qwertyuiop[]"
466 "asdfghjkl;'zxcvbnm8237tr28weufwldebvfv8734t07p";
467 static_assert(longCredType.size() > maxCredentialTypeLength);
468 auto jv = credentials::create(subject, issuer, longCredType);
469 env(jv, ter(temMALFORMED));
470 }
471
472 {
473 testcase("Credentials fail, URI length > 256.");
474 constexpr std::string_view longURI =
475 "abcdefghijklmnopqrstuvwxyz01234567890qwertyuiop[]"
476 "asdfghjkl;'zxcvbnm8237tr28weufwldebvfv8734t07p "
477 "9hfup;wDJFBVSD8f72 "
478 "pfhiusdovnbs;"
479 "djvbldafghwpEFHdjfaidfgio84763tfysgdvhjasbd "
480 "vujhgWQIE7F6WEUYFGWUKEYFVQW87FGWOEFWEFUYWVEF8723GFWEFB"
481 "WULE"
482 "fv28o37gfwEFB3872TFO8GSDSDVD";
483 static_assert(longURI.size() > maxCredentialURILength);
484 env(credentials::create(subject, issuer, credType),
485 credentials::uri(longURI),
487 }
488
489 {
490 testcase("Credentials fail, URI empty.");
491 env(credentials::create(subject, issuer, credType),
494 }
495
496 {
497 testcase("Credentials fail, expiration in the past.");
498 auto jv = credentials::create(subject, issuer, credType);
499 // current time in ripple epoch - 1s
500 uint32_t const t = env.current()
501 ->header()
502 .parentCloseTime.time_since_epoch()
503 .count() -
504 1;
505 jv[sfExpiration.jsonName] = t;
506 env(jv, ter(tecEXPIRED));
507 }
508
509 {
510 testcase("Credentials fail, invalid fee.");
511
512 auto jv = credentials::create(subject, issuer, credType);
513 jv[jss::Fee] = -1;
514 env(jv, ter(temBAD_FEE));
515 }
516
517 {
518 testcase("Credentials fail, duplicate.");
519 auto const jv = credentials::create(subject, issuer, credType);
520 env(jv);
521 env.close();
522 env(jv, ter(tecDUPLICATE));
523 env.close();
524
525 // check credential still present
526 auto const jle =
527 credentials::ledgerEntry(env, subject, issuer, credType);
528 BEAST_EXPECT(
529 jle.isObject() && jle.isMember(jss::result) &&
530 !jle[jss::result].isMember(jss::error) &&
531 jle[jss::result].isMember(jss::node) &&
532 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
533 jle[jss::result][jss::node]["LedgerEntryType"] ==
534 jss::Credential &&
535 jle[jss::result][jss::node][jss::Issuer] ==
536 issuer.human() &&
537 jle[jss::result][jss::node][jss::Subject] ==
538 subject.human() &&
539 jle[jss::result][jss::node]["CredentialType"] ==
540 strHex(std::string_view(credType)));
541 }
542
543 {
544 testcase("Credentials fail, directory full");
545 std::uint32_t const issuerSeq{env.seq(issuer) + 1};
546 env(ticket::create(issuer, 63));
547 env.close();
548
549 // Everything below can only be tested on open ledger.
550 auto const res1 = directory::bumpLastPage(
551 env,
553 keylet::ownerDir(issuer.id()),
555 BEAST_EXPECT(res1);
556
557 auto const jv = credentials::create(issuer, subject, credType);
558 env(jv, ter(tecDIR_FULL));
559 // Free one directory entry by using a ticket
560 env(noop(issuer), ticket::use(issuerSeq + 40));
561
562 // Fill subject directory
563 env(ticket::create(subject, 63));
564 auto const res2 = directory::bumpLastPage(
565 env,
567 keylet::ownerDir(subject.id()),
569 BEAST_EXPECT(res2);
570 env(jv, ter(tecDIR_FULL));
571
572 // End test
573 env.close();
574 }
575 }
576
577 {
578 using namespace jtx;
579 Env env{*this, features};
580
581 env.fund(XRP(5000), issuer);
582 env.close();
583
584 {
585 testcase("Credentials fail, subject doesn't exist.");
586 auto const jv = credentials::create(subject, issuer, credType);
587 env(jv, ter(tecNO_TARGET));
588 }
589 }
590
591 {
592 using namespace jtx;
593 Env env{*this, features};
594
595 auto const reserve = drops(env.current()->fees().reserve);
596 env.fund(reserve, subject, issuer);
597 env.close();
598
599 testcase("Credentials fail, not enough reserve.");
600 {
601 auto const jv = credentials::create(subject, issuer, credType);
603 env.close();
604 }
605 }
606 }
607
608 void
610 {
611 using namespace jtx;
612
613 char const credType[] = "abcde";
614 Account const issuer{"issuer"};
615 Account const subject{"subject"};
616 Account const other{"other"};
617
618 {
619 Env env{*this, features};
620
621 env.fund(XRP(5000), subject, issuer);
622
623 {
624 testcase("CredentialsAccept fail, Credential doesn't exist.");
625 env(credentials::accept(subject, issuer, credType),
627 env.close();
628 }
629
630 {
631 testcase("CredentialsAccept fail, invalid Issuer account.");
632 auto jv = credentials::accept(subject, issuer, credType);
633 jv[jss::Issuer] = to_string(xrpAccount());
634 env(jv, ter(temINVALID_ACCOUNT_ID));
635 env.close();
636 }
637
638 {
639 testcase(
640 "CredentialsAccept fail, invalid credentialType param.");
641 auto jv = credentials::accept(subject, issuer, "");
642 env(jv, ter(temMALFORMED));
643 }
644 }
645
646 {
647 Env env{*this, features};
648
649 env.fund(drops(env.current()->fees().accountReserve(1)), issuer);
650 env.fund(drops(env.current()->fees().accountReserve(0)), subject);
651 env.close();
652
653 {
654 testcase("CredentialsAccept fail, not enough reserve.");
655 env(credentials::create(subject, issuer, credType));
656 env.close();
657
658 env(credentials::accept(subject, issuer, credType),
660 env.close();
661
662 // check credential still present
663 auto const jle =
664 credentials::ledgerEntry(env, subject, issuer, credType);
665 BEAST_EXPECT(
666 jle.isObject() && jle.isMember(jss::result) &&
667 !jle[jss::result].isMember(jss::error) &&
668 jle[jss::result].isMember(jss::node) &&
669 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
670 jle[jss::result][jss::node]["LedgerEntryType"] ==
671 jss::Credential &&
672 jle[jss::result][jss::node][jss::Issuer] ==
673 issuer.human() &&
674 jle[jss::result][jss::node][jss::Subject] ==
675 subject.human() &&
676 jle[jss::result][jss::node]["CredentialType"] ==
677 strHex(std::string_view(credType)));
678 }
679 }
680
681 {
682 using namespace jtx;
683 Env env{*this, features};
684
685 env.fund(XRP(5000), subject, issuer);
686 env.close();
687
688 {
689 env(credentials::create(subject, issuer, credType));
690 env.close();
691
692 testcase("CredentialsAccept fail, invalid fee.");
693 auto jv = credentials::accept(subject, issuer, credType);
694 jv[jss::Fee] = -1;
695 env(jv, ter(temBAD_FEE));
696
697 testcase("CredentialsAccept fail, lsfAccepted already set.");
698 env(credentials::accept(subject, issuer, credType));
699 env.close();
700 env(credentials::accept(subject, issuer, credType),
702 env.close();
703
704 // check credential still present
705 auto const jle =
706 credentials::ledgerEntry(env, subject, issuer, credType);
707 BEAST_EXPECT(
708 jle.isObject() && jle.isMember(jss::result) &&
709 !jle[jss::result].isMember(jss::error) &&
710 jle[jss::result].isMember(jss::node) &&
711 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
712 jle[jss::result][jss::node]["LedgerEntryType"] ==
713 jss::Credential &&
714 jle[jss::result][jss::node][jss::Issuer] ==
715 issuer.human() &&
716 jle[jss::result][jss::node][jss::Subject] ==
717 subject.human() &&
718 jle[jss::result][jss::node]["CredentialType"] ==
719 strHex(std::string_view(credType)));
720 }
721
722 {
723 char const credType2[] = "efghi";
724
725 testcase("CredentialsAccept fail, expired credentials.");
726 auto jv = credentials::create(subject, issuer, credType2);
727 uint32_t const t = env.current()
728 ->header()
729 .parentCloseTime.time_since_epoch()
730 .count();
731 jv[sfExpiration.jsonName] = t;
732 env(jv);
733 env.close();
734
735 // credentials are expired now
736 env(credentials::accept(subject, issuer, credType2),
737 ter(tecEXPIRED));
738 env.close();
739
740 // check that expired credentials were deleted
741 auto const jDelCred =
742 credentials::ledgerEntry(env, subject, issuer, credType2);
743 BEAST_EXPECT(
744 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
745 jDelCred[jss::result].isMember(jss::error) &&
746 jDelCred[jss::result][jss::error] == "entryNotFound");
747
748 BEAST_EXPECT(ownerCount(env, issuer) == 0);
749 BEAST_EXPECT(ownerCount(env, subject) == 1);
750 }
751 }
752
753 {
754 using namespace jtx;
755 Env env{*this, features};
756
757 env.fund(XRP(5000), issuer, subject, other);
758 env.close();
759
760 {
761 testcase("CredentialsAccept fail, issuer doesn't exist.");
762 auto jv = credentials::create(subject, issuer, credType);
763 env(jv);
764 env.close();
765
766 // delete issuer
767 int const delta = env.seq(issuer) + 255;
768 for (int i = 0; i < delta; ++i)
769 env.close();
770 auto const acctDelFee{drops(env.current()->fees().increment)};
771 env(acctdelete(issuer, other), fee(acctDelFee));
772
773 // can't accept - no issuer account
774 jv = credentials::accept(subject, issuer, credType);
775 env(jv, ter(tecNO_ISSUER));
776 env.close();
777
778 // check that expired credentials were deleted
779 auto const jDelCred =
780 credentials::ledgerEntry(env, subject, issuer, credType);
781 BEAST_EXPECT(
782 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
783 jDelCred[jss::result].isMember(jss::error) &&
784 jDelCred[jss::result][jss::error] == "entryNotFound");
785 }
786 }
787 }
788
789 void
791 {
792 using namespace test::jtx;
793
794 char const credType[] = "abcde";
795 Account const issuer{"issuer"};
796 Account const subject{"subject"};
797 Account const other{"other"};
798
799 {
800 using namespace jtx;
801 Env env{*this, features};
802
803 env.fund(XRP(5000), subject, issuer, other);
804 env.close();
805
806 {
807 testcase("CredentialsDelete fail, no Credentials.");
808 env(credentials::deleteCred(subject, subject, issuer, credType),
810 env.close();
811 }
812
813 {
814 testcase("CredentialsDelete fail, invalid Subject account.");
815 auto jv =
816 credentials::deleteCred(subject, subject, issuer, credType);
817 jv[jss::Subject] = to_string(xrpAccount());
818 env(jv, ter(temINVALID_ACCOUNT_ID));
819 env.close();
820 }
821
822 {
823 testcase("CredentialsDelete fail, invalid Issuer account.");
824 auto jv =
825 credentials::deleteCred(subject, subject, issuer, credType);
826 jv[jss::Issuer] = to_string(xrpAccount());
827 env(jv, ter(temINVALID_ACCOUNT_ID));
828 env.close();
829 }
830
831 {
832 testcase(
833 "CredentialsDelete fail, invalid credentialType param.");
834 auto jv = credentials::deleteCred(subject, subject, issuer, "");
835 env(jv, ter(temMALFORMED));
836 }
837
838 {
839 char const credType2[] = "fghij";
840
841 env(credentials::create(subject, issuer, credType2));
842 env.close();
843
844 // Other account can't delete credentials without expiration
845 env(credentials::deleteCred(other, subject, issuer, credType2),
847 env.close();
848
849 // check credential still present
850 auto const jle =
851 credentials::ledgerEntry(env, subject, issuer, credType2);
852 BEAST_EXPECT(
853 jle.isObject() && jle.isMember(jss::result) &&
854 !jle[jss::result].isMember(jss::error) &&
855 jle[jss::result].isMember(jss::node) &&
856 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
857 jle[jss::result][jss::node]["LedgerEntryType"] ==
858 jss::Credential &&
859 jle[jss::result][jss::node][jss::Issuer] ==
860 issuer.human() &&
861 jle[jss::result][jss::node][jss::Subject] ==
862 subject.human() &&
863 jle[jss::result][jss::node]["CredentialType"] ==
864 strHex(std::string_view(credType2)));
865 }
866
867 {
868 testcase("CredentialsDelete fail, time not expired yet.");
869
870 auto jv = credentials::create(subject, issuer, credType);
871 // current time in ripple epoch + 1000s
872 uint32_t const t = env.current()
873 ->header()
874 .parentCloseTime.time_since_epoch()
875 .count() +
876 1000;
877 jv[sfExpiration.jsonName] = t;
878 env(jv);
879 env.close();
880
881 // Other account can't delete credentials that not expired
882 env(credentials::deleteCred(other, subject, issuer, credType),
884 env.close();
885
886 // check credential still present
887 auto const jle =
888 credentials::ledgerEntry(env, subject, issuer, credType);
889 BEAST_EXPECT(
890 jle.isObject() && jle.isMember(jss::result) &&
891 !jle[jss::result].isMember(jss::error) &&
892 jle[jss::result].isMember(jss::node) &&
893 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
894 jle[jss::result][jss::node]["LedgerEntryType"] ==
895 jss::Credential &&
896 jle[jss::result][jss::node][jss::Issuer] ==
897 issuer.human() &&
898 jle[jss::result][jss::node][jss::Subject] ==
899 subject.human() &&
900 jle[jss::result][jss::node]["CredentialType"] ==
901 strHex(std::string_view(credType)));
902 }
903
904 {
905 testcase("CredentialsDelete fail, no Issuer and Subject.");
906
907 auto jv =
908 credentials::deleteCred(subject, subject, issuer, credType);
909 jv.removeMember(jss::Subject);
910 jv.removeMember(jss::Issuer);
911 env(jv, ter(temMALFORMED));
912 env.close();
913 }
914
915 {
916 testcase("CredentialsDelete fail, invalid fee.");
917
918 auto jv =
919 credentials::deleteCred(subject, subject, issuer, credType);
920 jv[jss::Fee] = -1;
921 env(jv, ter(temBAD_FEE));
922 env.close();
923 }
924
925 {
926 testcase("deleteSLE fail, bad SLE.");
928 env.current().get(), ApplyFlags::tapNONE);
929 auto ter = xrpl::credentials::deleteSLE(*view, {}, env.journal);
930 BEAST_EXPECT(ter == tecNO_ENTRY);
931 }
932 }
933 }
934
935 void
937 {
938 using namespace test::jtx;
939
940 char const credType[] = "abcde";
941 Account const issuer{"issuer"};
942 Account const subject{"subject"};
943
944 {
945 using namespace jtx;
946 Env env{*this, features};
947
948 env.fund(XRP(5000), subject, issuer);
949 env.close();
950
951 {
952 testcase("Credentials fail, Feature is not enabled.");
953 env(credentials::create(subject, issuer, credType),
955 env(credentials::accept(subject, issuer, credType),
957 env(credentials::deleteCred(subject, subject, issuer, credType),
959 }
960 }
961 }
962
963 void
965 {
966 using namespace test::jtx;
967
968 char const credType[] = "abcde";
969 Account const issuer{"issuer"};
970 Account const subject{"subject"};
971
972 {
973 using namespace jtx;
974 Env env{*this};
975
976 env.fund(XRP(5000), subject, issuer);
977 env.close();
978
979 env(credentials::create(subject, issuer, credType));
980 env.close();
981
982 env(credentials::accept(subject, issuer, credType));
983 env.close();
984
985 testcase("account_tx");
986
987 std::string txHash0, txHash1;
988 {
989 Json::Value params;
990 params[jss::account] = subject.human();
991 auto const jv = env.rpc(
992 "json", "account_tx", to_string(params))[jss::result];
993
994 BEAST_EXPECT(jv[jss::transactions].size() == 4);
995 auto const& tx0(jv[jss::transactions][0u][jss::tx]);
996 BEAST_EXPECT(
997 tx0[jss::TransactionType] == jss::CredentialAccept);
998 auto const& tx1(jv[jss::transactions][1u][jss::tx]);
999 BEAST_EXPECT(
1000 tx1[jss::TransactionType] == jss::CredentialCreate);
1001 txHash0 = tx0[jss::hash].asString();
1002 txHash1 = tx1[jss::hash].asString();
1003 }
1004
1005 {
1006 Json::Value params;
1007 params[jss::account] = issuer.human();
1008 auto const jv = env.rpc(
1009 "json", "account_tx", to_string(params))[jss::result];
1010
1011 BEAST_EXPECT(jv[jss::transactions].size() == 4);
1012 auto const& tx0(jv[jss::transactions][0u][jss::tx]);
1013 BEAST_EXPECT(
1014 tx0[jss::TransactionType] == jss::CredentialAccept);
1015 auto const& tx1(jv[jss::transactions][1u][jss::tx]);
1016 BEAST_EXPECT(
1017 tx1[jss::TransactionType] == jss::CredentialCreate);
1018
1019 BEAST_EXPECT(txHash0 == tx0[jss::hash].asString());
1020 BEAST_EXPECT(txHash1 == tx1[jss::hash].asString());
1021 }
1022
1023 testcase("account_objects");
1024 std::string objectIdx;
1025 {
1026 Json::Value params;
1027 params[jss::account] = subject.human();
1028 auto jv = env.rpc(
1029 "json", "account_objects", to_string(params))[jss::result];
1030
1031 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
1032 auto const& object(jv[jss::account_objects][0u]);
1033
1034 BEAST_EXPECT(
1035 object["LedgerEntryType"].asString() == jss::Credential);
1036 objectIdx = object[jss::index].asString();
1037 }
1038
1039 {
1040 Json::Value params;
1041 params[jss::account] = issuer.human();
1042 auto jv = env.rpc(
1043 "json", "account_objects", to_string(params))[jss::result];
1044
1045 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
1046 auto const& object(jv[jss::account_objects][0u]);
1047
1048 BEAST_EXPECT(
1049 object["LedgerEntryType"].asString() == jss::Credential);
1050 BEAST_EXPECT(objectIdx == object[jss::index].asString());
1051 }
1052 }
1053 }
1054
1055 void
1057 {
1058 using namespace test::jtx;
1059
1060 bool const enabled = features[fixInvalidTxFlags];
1061 testcase(
1062 std::string("Test flag, fix ") +
1063 (enabled ? "enabled" : "disabled"));
1064
1065 char const credType[] = "abcde";
1066 Account const issuer{"issuer"};
1067 Account const subject{"subject"};
1068
1069 {
1070 using namespace jtx;
1071 Env env{*this, features};
1072
1073 env.fund(XRP(5000), subject, issuer);
1074 env.close();
1075
1076 {
1077 ter const expected(
1078 enabled ? TER(temINVALID_FLAG) : TER(tesSUCCESS));
1079 env(credentials::create(subject, issuer, credType),
1081 expected);
1082 env(credentials::accept(subject, issuer, credType),
1084 expected);
1085 env(credentials::deleteCred(subject, subject, issuer, credType),
1087 expected);
1088 }
1089 }
1090 }
1091
1092 void
1093 run() override
1094 {
1095 using namespace test::jtx;
1100 testCreateFailed(all - fixDirectoryLimit);
1103 testFeatureFailed(all - featureCredentials);
1104 testFlags(all - fixInvalidTxFlags);
1105 testFlags(all);
1106 testRPC();
1107 }
1108};
1109
1110BEAST_DEFINE_TESTSUITE(Credentials, app, xrpl);
1111
1112} // namespace test
1113} // namespace xrpl
Represents a JSON value.
Definition json_value.h:131
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
Immutable cryptographic account descriptor.
Definition Account.h:20
A transaction testing environment.
Definition Env.h:102
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:272
Set the fee on a JTx.
Definition fee.h:18
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 is_same_v
TER deleteSLE(ApplyView &view, std::shared_ptr< SLE > const &sleCredential, beast::Journal j)
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:356
Json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:29
Json::Value deleteCred(jtx::Account const &acc, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:43
Json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:59
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:13
Keylet keylet(test::jtx::Account const &subject, test::jtx::Account const &issuer, std::string_view credType)
Definition credentials.h:15
auto maximumPageIndex(Env const &env) -> std::uint64_t
Definition directory.h:51
auto bumpLastPage(Env &env, std::uint64_t newLastPage, Keylet directory, std::function< bool(ApplyView &, uint256, std::uint64_t)> adjust) -> Expected< void, Error >
Move the position of the last page in the user's directory on open ledger to newLastPage.
Definition directory.cpp:11
bool adjustOwnerNode(ApplyView &view, uint256 key, std::uint64_t page)
Implementation of adjust for the most common ledger entry, i.e.
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)
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
FeatureBitset testable_amendments()
Definition Env.h:55
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
bool checkVL(Slice const &result, std::string const &expected)
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 const tfTransferable
Definition TxFlags.h:123
constexpr std::uint32_t tfPassive
Definition TxFlags.h:79
std::size_t constexpr maxCredentialURILength
The maximum length of a URI inside a Credential.
Definition Protocol.h:219
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
TERSubset< CanCvtToTER > TER
Definition TER.h:630
@ tapNONE
Definition ApplyView.h:12
@ temBAD_FEE
Definition TER.h:73
@ temINVALID_FLAG
Definition TER.h:92
@ temMALFORMED
Definition TER.h:68
@ temINVALID_ACCOUNT_ID
Definition TER.h:100
@ temDISABLED
Definition TER.h:95
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecDIR_FULL
Definition TER.h:269
@ tecNO_ENTRY
Definition TER.h:288
@ tecNO_TARGET
Definition TER.h:286
@ tecEXPIRED
Definition TER.h:296
@ tecINSUFFICIENT_RESERVE
Definition TER.h:289
@ tecNO_PERMISSION
Definition TER.h:287
@ tecNO_ISSUER
Definition TER.h:281
@ tecDUPLICATE
Definition TER.h:297
@ lsfAccepted
constexpr std::uint32_t const tfSellNFToken
Definition TxFlags.h:211
@ tesSUCCESS
Definition TER.h:226
std::size_t constexpr maxCredentialTypeLength
The maximum length of a CredentialType inside a Credential.
Definition Protocol.h:222
T size(T... args)
void run() override
Runs the suite.
void testDeleteFailed(FeatureBitset features)
void testCredentialsDelete(FeatureBitset features)
void testCreateFailed(FeatureBitset features)
void testAcceptFailed(FeatureBitset features)
void testFlags(FeatureBitset features)
void testFeatureFailed(FeatureBitset features)
void testSuccessful(FeatureBitset features)