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 ripple {
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 ->info()
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 ->info()
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 ->info()
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 ->info()
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 =
930 ripple::credentials::deleteSLE(*view, {}, env.journal);
931 BEAST_EXPECT(ter == tecNO_ENTRY);
932 }
933 }
934 }
935
936 void
938 {
939 using namespace test::jtx;
940
941 char const credType[] = "abcde";
942 Account const issuer{"issuer"};
943 Account const subject{"subject"};
944
945 {
946 using namespace jtx;
947 Env env{*this, features};
948
949 env.fund(XRP(5000), subject, issuer);
950 env.close();
951
952 {
953 testcase("Credentials fail, Feature is not enabled.");
954 env(credentials::create(subject, issuer, credType),
956 env(credentials::accept(subject, issuer, credType),
958 env(credentials::deleteCred(subject, subject, issuer, credType),
960 }
961 }
962 }
963
964 void
966 {
967 using namespace test::jtx;
968
969 char const credType[] = "abcde";
970 Account const issuer{"issuer"};
971 Account const subject{"subject"};
972
973 {
974 using namespace jtx;
975 Env env{*this};
976
977 env.fund(XRP(5000), subject, issuer);
978 env.close();
979
980 env(credentials::create(subject, issuer, credType));
981 env.close();
982
983 env(credentials::accept(subject, issuer, credType));
984 env.close();
985
986 testcase("account_tx");
987
988 std::string txHash0, txHash1;
989 {
990 Json::Value params;
991 params[jss::account] = subject.human();
992 auto const jv = env.rpc(
993 "json", "account_tx", to_string(params))[jss::result];
994
995 BEAST_EXPECT(jv[jss::transactions].size() == 4);
996 auto const& tx0(jv[jss::transactions][0u][jss::tx]);
997 BEAST_EXPECT(
998 tx0[jss::TransactionType] == jss::CredentialAccept);
999 auto const& tx1(jv[jss::transactions][1u][jss::tx]);
1000 BEAST_EXPECT(
1001 tx1[jss::TransactionType] == jss::CredentialCreate);
1002 txHash0 = tx0[jss::hash].asString();
1003 txHash1 = tx1[jss::hash].asString();
1004 }
1005
1006 {
1007 Json::Value params;
1008 params[jss::account] = issuer.human();
1009 auto const jv = env.rpc(
1010 "json", "account_tx", to_string(params))[jss::result];
1011
1012 BEAST_EXPECT(jv[jss::transactions].size() == 4);
1013 auto const& tx0(jv[jss::transactions][0u][jss::tx]);
1014 BEAST_EXPECT(
1015 tx0[jss::TransactionType] == jss::CredentialAccept);
1016 auto const& tx1(jv[jss::transactions][1u][jss::tx]);
1017 BEAST_EXPECT(
1018 tx1[jss::TransactionType] == jss::CredentialCreate);
1019
1020 BEAST_EXPECT(txHash0 == tx0[jss::hash].asString());
1021 BEAST_EXPECT(txHash1 == tx1[jss::hash].asString());
1022 }
1023
1024 testcase("account_objects");
1025 std::string objectIdx;
1026 {
1027 Json::Value params;
1028 params[jss::account] = subject.human();
1029 auto jv = env.rpc(
1030 "json", "account_objects", to_string(params))[jss::result];
1031
1032 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
1033 auto const& object(jv[jss::account_objects][0u]);
1034
1035 BEAST_EXPECT(
1036 object["LedgerEntryType"].asString() == jss::Credential);
1037 objectIdx = object[jss::index].asString();
1038 }
1039
1040 {
1041 Json::Value params;
1042 params[jss::account] = issuer.human();
1043 auto jv = env.rpc(
1044 "json", "account_objects", to_string(params))[jss::result];
1045
1046 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
1047 auto const& object(jv[jss::account_objects][0u]);
1048
1049 BEAST_EXPECT(
1050 object["LedgerEntryType"].asString() == jss::Credential);
1051 BEAST_EXPECT(objectIdx == object[jss::index].asString());
1052 }
1053 }
1054 }
1055
1056 void
1058 {
1059 using namespace test::jtx;
1060
1061 bool const enabled = features[fixInvalidTxFlags];
1062 testcase(
1063 std::string("Test flag, fix ") +
1064 (enabled ? "enabled" : "disabled"));
1065
1066 char const credType[] = "abcde";
1067 Account const issuer{"issuer"};
1068 Account const subject{"subject"};
1069
1070 {
1071 using namespace jtx;
1072 Env env{*this, features};
1073
1074 env.fund(XRP(5000), subject, issuer);
1075 env.close();
1076
1077 {
1078 ter const expected(
1079 enabled ? TER(temINVALID_FLAG) : TER(tesSUCCESS));
1080 env(credentials::create(subject, issuer, credType),
1082 expected);
1083 env(credentials::accept(subject, issuer, credType),
1085 expected);
1086 env(credentials::deleteCred(subject, subject, issuer, credType),
1088 expected);
1089 }
1090 }
1091 }
1092
1093 void
1094 run() override
1095 {
1096 using namespace test::jtx;
1101 testCreateFailed(all - fixDirectoryLimit);
1104 testFeatureFailed(all - featureCredentials);
1105 testFlags(all - fixInvalidTxFlags);
1106 testFlags(all);
1107 testRPC();
1108 }
1109};
1110
1111BEAST_DEFINE_TESTSUITE(Credentials, app, ripple);
1112
1113} // namespace test
1114} // namespace ripple
Represents a JSON value.
Definition json_value.h:130
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:271
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:355
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
Keylet keylet(test::jtx::Account const &subject, test::jtx::Account const &issuer, std::string_view credType)
Definition credentials.h:15
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
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
auto maximumPageIndex(Env const &env) -> std::uint64_t
Definition directory.h:51
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)
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
bool checkVL(Slice const &result, std::string const &expected)
FeatureBitset testable_amendments()
Definition Env.h:55
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
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
AccountID const & xrpAccount()
Compute AccountID from public key.
std::size_t constexpr maxCredentialURILength
The maximum length of a URI inside a Credential.
Definition Protocol.h:84
constexpr std::uint32_t const tfSellNFToken
Definition TxFlags.h:211
constexpr std::uint32_t tfPassive
Definition TxFlags.h:79
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
std::size_t constexpr maxCredentialTypeLength
The maximum length of a CredentialType inside a Credential.
Definition Protocol.h:87
@ tecNO_ENTRY
Definition TER.h:288
@ tecNO_ISSUER
Definition TER.h:281
@ tecNO_TARGET
Definition TER.h:286
@ tecDIR_FULL
Definition TER.h:269
@ tecDUPLICATE
Definition TER.h:297
@ tecNO_PERMISSION
Definition TER.h:287
@ 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
@ tapNONE
Definition ApplyView.h:12
TERSubset< CanCvtToTER > TER
Definition TER.h:630
constexpr std::uint32_t const tfTransferable
Definition TxFlags.h:123
@ temBAD_FEE
Definition TER.h:73
@ temMALFORMED
Definition TER.h:68
@ temINVALID_FLAG
Definition TER.h:92
@ temDISABLED
Definition TER.h:95
@ temINVALID_ACCOUNT_ID
Definition TER.h:100
T size(T... args)
void testAcceptFailed(FeatureBitset features)
void testSuccessful(FeatureBitset features)
void testDeleteFailed(FeatureBitset features)
void testFeatureFailed(FeatureBitset features)
void testFlags(FeatureBitset features)
void testCredentialsDelete(FeatureBitset features)
void run() override
Runs the suite.
void testCreateFailed(FeatureBitset features)