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