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