xrpld
Loading...
Searching...
No Matches
Credentials_test.cpp
1
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/acctdelete.h>
6#include <test/jtx/amount.h>
7#include <test/jtx/credentials.h>
8#include <test/jtx/deposit.h>
9#include <test/jtx/directory.h>
10#include <test/jtx/fee.h>
11#include <test/jtx/noop.h>
12#include <test/jtx/pay.h>
13#include <test/jtx/permissioned_domains.h>
14#include <test/jtx/ter.h>
15#include <test/jtx/ticket.h>
16#include <test/jtx/txflags.h>
17#include <test/jtx/vault.h>
18
19#include <xrpl/basics/strHex.h>
20#include <xrpl/beast/unit_test/suite.h>
21#include <xrpl/json/to_string.h>
22#include <xrpl/ledger/ApplyView.h>
23#include <xrpl/ledger/ApplyViewImpl.h>
24#include <xrpl/ledger/helpers/CredentialHelpers.h>
25#include <xrpl/protocol/AccountID.h>
26#include <xrpl/protocol/Feature.h>
27#include <xrpl/protocol/Indexes.h>
28#include <xrpl/protocol/Issue.h>
29#include <xrpl/protocol/LedgerFormats.h>
30#include <xrpl/protocol/Protocol.h>
31#include <xrpl/protocol/SField.h>
32#include <xrpl/protocol/STTx.h>
33#include <xrpl/protocol/TER.h>
34#include <xrpl/protocol/TxFlags.h>
35#include <xrpl/protocol/jss.h>
36
37#include <cstdint>
38#include <cstring>
39#include <memory>
40#include <string_view>
41
42namespace xrpl::test {
43
45{
46 void
48 {
49 using namespace test::jtx;
50
51 char const credType[] = "abcde";
52 char const uri[] = "uri";
53
54 Account const issuer{"issuer"};
55 Account const subject{"subject"};
56 Account const other{"other"};
57
58 Env env{*this, features};
59
60 {
61 testcase("Create for subject.");
62
63 auto const credKey = credentials::keylet(subject, issuer, credType);
64
65 env.fund(XRP(5000), subject, issuer, other);
66 env.close();
67
68 // Test Create credentials
69 env(credentials::create(subject, issuer, credType), credentials::Uri(uri));
70 env.close();
71 {
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(!sleCred->getFieldU32(sfFlags));
80 BEAST_EXPECT(ownerCount(env, issuer) == 1);
81 BEAST_EXPECT(!ownerCount(env, subject));
82 BEAST_EXPECT(checkVL(sleCred, sfCredentialType, credType));
83 BEAST_EXPECT(checkVL(sleCred, sfURI, uri));
84 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
85 BEAST_EXPECT(
86 jle.isObject() && jle.isMember(jss::result) &&
87 !jle[jss::result].isMember(jss::error) &&
88 jle[jss::result].isMember(jss::node) &&
89 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
90 jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential &&
91 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
92 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
93 jle[jss::result][jss::node]["CredentialType"] ==
94 strHex(std::string_view(credType)));
95 }
96
97 env(credentials::accept(subject, issuer, credType));
98 env.close();
99 {
100 // check switching owner of the credentials from issuer to
101 // subject
102 auto const sleCred = env.le(credKey);
103 BEAST_EXPECT(static_cast<bool>(sleCred));
104 if (!sleCred)
105 return;
106
107 BEAST_EXPECT(sleCred->getAccountID(sfSubject) == subject.id());
108 BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id());
109 BEAST_EXPECT(!ownerCount(env, issuer));
110 BEAST_EXPECT(ownerCount(env, subject) == 1);
111 BEAST_EXPECT(checkVL(sleCred, sfCredentialType, credType));
112 BEAST_EXPECT(checkVL(sleCred, sfURI, uri));
113 BEAST_EXPECT(sleCred->getFieldU32(sfFlags) == lsfAccepted);
114 }
115
116 env(credentials::deleteCred(subject, subject, issuer, credType));
117 env.close();
118 {
119 BEAST_EXPECT(!env.le(credKey));
120 BEAST_EXPECT(!ownerCount(env, issuer));
121 BEAST_EXPECT(!ownerCount(env, subject));
122
123 // check no credential exists anymore
124 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
125 BEAST_EXPECT(
126 jle.isObject() && jle.isMember(jss::result) &&
127 jle[jss::result].isMember(jss::error) &&
128 jle[jss::result][jss::error] == "entryNotFound");
129 }
130 }
131
132 {
133 testcase("Create for themself.");
134
135 auto const credKey = credentials::keylet(issuer, issuer, credType);
136
137 env(credentials::create(issuer, issuer, credType), credentials::Uri(uri));
138 env.close();
139 {
140 auto const sleCred = env.le(credKey);
141 BEAST_EXPECT(static_cast<bool>(sleCred));
142 if (!sleCred)
143 return;
144
145 BEAST_EXPECT(sleCred->getAccountID(sfSubject) == issuer.id());
146 BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id());
147 BEAST_EXPECT(sleCred->isFlag(lsfAccepted));
148 BEAST_EXPECT(
149 sleCred->getFieldU64(sfIssuerNode) == sleCred->getFieldU64(sfSubjectNode));
150 BEAST_EXPECT(ownerCount(env, issuer) == 1);
151 BEAST_EXPECT(checkVL(sleCred, sfCredentialType, credType));
152 BEAST_EXPECT(checkVL(sleCred, sfURI, uri));
153 auto const jle = credentials::ledgerEntry(env, issuer, issuer, credType);
154 BEAST_EXPECT(
155 jle.isObject() && jle.isMember(jss::result) &&
156 !jle[jss::result].isMember(jss::error) &&
157 jle[jss::result].isMember(jss::node) &&
158 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
159 jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential &&
160 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
161 jle[jss::result][jss::node][jss::Subject] == issuer.human() &&
162 jle[jss::result][jss::node]["CredentialType"] ==
163 strHex(std::string_view(credType)));
164 }
165
166 env(credentials::deleteCred(issuer, issuer, issuer, credType));
167 env.close();
168 {
169 BEAST_EXPECT(!env.le(credKey));
170 BEAST_EXPECT(!ownerCount(env, issuer));
171
172 // check no credential exists anymore
173 auto const jle = credentials::ledgerEntry(env, issuer, issuer, credType);
174 BEAST_EXPECT(
175 jle.isObject() && jle.isMember(jss::result) &&
176 jle[jss::result].isMember(jss::error) &&
177 jle[jss::result][jss::error] == "entryNotFound");
178 }
179 }
180 }
181
182 void
184 {
185 using namespace test::jtx;
186
187 char const credType[] = "abcde";
188
189 Account const issuer{"issuer"};
190 Account const subject{"subject"};
191 Account const other{"other"};
192
193 Env env{*this, features};
194
195 // fund subject and issuer
196 env.fund(XRP(5000), issuer, subject, other);
197 env.close();
198
199 {
200 testcase("Delete issuer before accept");
201
202 auto const credKey = credentials::keylet(subject, issuer, credType);
203 env(credentials::create(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) &&
225 jle[jss::result].isMember(jss::error) &&
226 jle[jss::result][jss::error] == "entryNotFound");
227 }
228
229 // resurrection
230 env.fund(XRP(5000), issuer);
231 env.close();
232 }
233
234 {
235 testcase("Delete issuer after accept");
236
237 auto const credKey = credentials::keylet(subject, issuer, credType);
238 env(credentials::create(subject, issuer, credType));
239 env.close();
240 env(credentials::accept(subject, issuer, credType));
241 env.close();
242
243 // delete issuer
244 {
245 int const delta = env.seq(issuer) + 255;
246 for (int i = 0; i < delta; ++i)
247 env.close();
248 auto const acctDelFee{drops(env.current()->fees().increment)};
249 env(acctdelete(issuer, other), Fee(acctDelFee));
250 env.close();
251 }
252
253 // check credentials deleted too
254 {
255 BEAST_EXPECT(!env.le(credKey));
256 BEAST_EXPECT(!ownerCount(env, subject));
257
258 // check no credential exists anymore
259 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
260 BEAST_EXPECT(
261 jle.isObject() && jle.isMember(jss::result) &&
262 jle[jss::result].isMember(jss::error) &&
263 jle[jss::result][jss::error] == "entryNotFound");
264 }
265
266 // resurrection
267 env.fund(XRP(5000), issuer);
268 env.close();
269 }
270
271 {
272 testcase("Delete subject before accept");
273
274 auto const credKey = credentials::keylet(subject, issuer, credType);
275 env(credentials::create(subject, issuer, credType));
276 env.close();
277
278 // delete subject
279 {
280 int const delta = env.seq(subject) + 255;
281 for (int i = 0; i < delta; ++i)
282 env.close();
283 auto const acctDelFee{drops(env.current()->fees().increment)};
284 env(acctdelete(subject, other), Fee(acctDelFee));
285 env.close();
286 }
287
288 // check credentials deleted too
289 {
290 BEAST_EXPECT(!env.le(credKey));
291 BEAST_EXPECT(!ownerCount(env, issuer));
292
293 // check no credential exists anymore
294 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
295 BEAST_EXPECT(
296 jle.isObject() && jle.isMember(jss::result) &&
297 jle[jss::result].isMember(jss::error) &&
298 jle[jss::result][jss::error] == "entryNotFound");
299 }
300
301 // resurrection
302 env.fund(XRP(5000), subject);
303 env.close();
304 }
305
306 {
307 testcase("Delete subject after accept");
308
309 auto const credKey = credentials::keylet(subject, issuer, credType);
310 env(credentials::create(subject, issuer, credType));
311 env.close();
312 env(credentials::accept(subject, issuer, credType));
313 env.close();
314
315 // delete subject
316 {
317 int const delta = env.seq(subject) + 255;
318 for (int i = 0; i < delta; ++i)
319 env.close();
320 auto const acctDelFee{drops(env.current()->fees().increment)};
321 env(acctdelete(subject, other), Fee(acctDelFee));
322 env.close();
323 }
324
325 // check credentials deleted too
326 {
327 BEAST_EXPECT(!env.le(credKey));
328 BEAST_EXPECT(!ownerCount(env, issuer));
329
330 // check no credential exists anymore
331 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
332 BEAST_EXPECT(
333 jle.isObject() && jle.isMember(jss::result) &&
334 jle[jss::result].isMember(jss::error) &&
335 jle[jss::result][jss::error] == "entryNotFound");
336 }
337
338 // resurrection
339 env.fund(XRP(5000), subject);
340 env.close();
341 }
342
343 {
344 testcase("Delete by other");
345
346 auto const credKey = credentials::keylet(subject, issuer, credType);
347 auto jv = credentials::create(subject, issuer, credType);
348 uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count();
349 jv[sfExpiration.jsonName] = t + 20;
350 env(jv);
351
352 // time advance
353 env.close();
354 env.close();
355 env.close();
356
357 // Other account delete credentials
358 env(credentials::deleteCred(other, subject, issuer, credType));
359 env.close();
360
361 // check credentials object
362 {
363 BEAST_EXPECT(!env.le(credKey));
364 BEAST_EXPECT(!ownerCount(env, issuer));
365 BEAST_EXPECT(!ownerCount(env, subject));
366
367 // check no credential exists anymore
368 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
369 BEAST_EXPECT(
370 jle.isObject() && jle.isMember(jss::result) &&
371 jle[jss::result].isMember(jss::error) &&
372 jle[jss::result][jss::error] == "entryNotFound");
373 }
374 }
375
376 {
377 testcase("Delete by subject");
378
379 env(credentials::create(subject, issuer, credType));
380 env.close();
381
382 // Subject can delete
383 env(credentials::deleteCred(subject, subject, issuer, credType));
384 env.close();
385 {
386 auto const credKey = credentials::keylet(subject, issuer, credType);
387 BEAST_EXPECT(!env.le(credKey));
388 BEAST_EXPECT(!ownerCount(env, subject));
389 BEAST_EXPECT(!ownerCount(env, issuer));
390 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
391 BEAST_EXPECT(
392 jle.isObject() && jle.isMember(jss::result) &&
393 jle[jss::result].isMember(jss::error) &&
394 jle[jss::result][jss::error] == "entryNotFound");
395 }
396 }
397
398 {
399 testcase("Delete by issuer");
400 env(credentials::create(subject, issuer, credType));
401 env.close();
402
403 env(credentials::deleteCred(issuer, subject, issuer, credType));
404 env.close();
405 {
406 auto const credKey = credentials::keylet(subject, issuer, credType);
407 BEAST_EXPECT(!env.le(credKey));
408 BEAST_EXPECT(!ownerCount(env, subject));
409 BEAST_EXPECT(!ownerCount(env, issuer));
410 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
411 BEAST_EXPECT(
412 jle.isObject() && jle.isMember(jss::result) &&
413 jle[jss::result].isMember(jss::error) &&
414 jle[jss::result][jss::error] == "entryNotFound");
415 }
416 }
417 }
418
419 void
421 {
422 using namespace test::jtx;
423
424 char const credType[] = "abcde";
425
426 Account const issuer{"issuer"};
427 Account const subject{"subject"};
428
429 {
430 Env env{*this, features};
431
432 env.fund(XRP(5000), subject, issuer);
433 env.close();
434
435 {
436 testcase("Credentials fail, no subject param.");
437 auto jv = credentials::create(subject, issuer, credType);
438 jv.removeMember(jss::Subject);
439 env(jv, Ter(temMALFORMED));
440 }
441
442 {
443 auto jv = credentials::create(subject, issuer, credType);
444 jv[jss::Subject] = to_string(xrpAccount());
445 env(jv, Ter(temMALFORMED));
446 }
447
448 {
449 testcase("Credentials fail, no credentialType param.");
450 auto jv = credentials::create(subject, issuer, credType);
451 jv.removeMember(sfCredentialType.jsonName);
452 env(jv, Ter(temMALFORMED));
453 }
454
455 {
456 testcase("Credentials fail, empty credentialType param.");
457 auto jv = credentials::create(subject, issuer, "");
458 env(jv, Ter(temMALFORMED));
459 }
460
461 {
462 testcase(
463 "Credentials fail, credentialType length > "
464 "maxCredentialTypeLength.");
465 static constexpr std::string_view kLongCredType =
466 "abcdefghijklmnopqrstuvwxyz01234567890qwertyuiop[]"
467 "asdfghjkl;'zxcvbnm8237tr28weufwldebvfv8734t07p";
468 static_assert(kLongCredType.size() > kMaxCredentialTypeLength);
469 auto jv = credentials::create(subject, issuer, kLongCredType);
470 env(jv, Ter(temMALFORMED));
471 }
472
473 {
474 testcase("Credentials fail, URI length > 256.");
475 static constexpr std::string_view kLongUri =
476 "abcdefghijklmnopqrstuvwxyz01234567890qwertyuiop[]"
477 "asdfghjkl;'zxcvbnm8237tr28weufwldebvfv8734t07p "
478 "9hfup;wDJFBVSD8f72 "
479 "pfhiusdovnbs;"
480 "djvbldafghwpEFHdjfaidfgio84763tfysgdvhjasbd "
481 "vujhgWQIE7F6WEUYFGWUKEYFVQW87FGWOEFWEFUYWVEF8723GFWEFB"
482 "WULE"
483 "fv28o37gfwEFB3872TFO8GSDSDVD";
484 static_assert(kLongUri.size() > kMaxCredentialUriLength);
485 env(credentials::create(subject, issuer, credType),
486 credentials::Uri(kLongUri),
488 }
489
490 {
491 testcase("Credentials fail, URI empty.");
492 env(credentials::create(subject, issuer, credType),
495 }
496
497 {
498 testcase("Credentials fail, expiration in the past.");
499 auto jv = credentials::create(subject, issuer, credType);
500 // current time in XRPL epoch - 1s
501 uint32_t const t =
502 env.current()->header().parentCloseTime.time_since_epoch().count() - 1;
503 jv[sfExpiration.jsonName] = t;
504 env(jv, Ter(tecEXPIRED));
505 }
506
507 {
508 testcase("Credentials fail, invalid fee.");
509
510 auto jv = credentials::create(subject, issuer, credType);
511 jv[jss::Fee] = -1;
512 env(jv, Ter(temBAD_FEE));
513 }
514
515 {
516 testcase("Credentials fail, duplicate.");
517 auto const jv = credentials::create(subject, issuer, credType);
518 env(jv);
519 env.close();
520 env(jv, Ter(tecDUPLICATE));
521 env.close();
522
523 // check credential still present
524 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
525 BEAST_EXPECT(
526 jle.isObject() && jle.isMember(jss::result) &&
527 !jle[jss::result].isMember(jss::error) &&
528 jle[jss::result].isMember(jss::node) &&
529 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
530 jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential &&
531 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
532 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
533 jle[jss::result][jss::node]["CredentialType"] ==
534 strHex(std::string_view(credType)));
535 }
536
537 {
538 testcase("Credentials fail, directory full");
539 std::uint32_t const issuerSeq{env.seq(issuer) + 1};
540 env(ticket::create(issuer, 63));
541 env.close();
542
543 // Everything below can only be tested on open ledger.
544 auto const res1 = directory::bumpLastPage(
545 env,
547 keylet::ownerDir(issuer.id()),
549 BEAST_EXPECT(res1);
550
551 // NOLINTNEXTLINE(readability-suspicious-call-argument)
552 auto const jv = credentials::create(issuer, subject, credType);
553 env(jv, Ter(tecDIR_FULL));
554 // Free one directory entry by using a ticket
555 env(noop(issuer), ticket::Use(issuerSeq + 40));
556
557 // Fill subject directory
558 env(ticket::create(subject, 63));
559 auto const res2 = directory::bumpLastPage(
560 env,
562 keylet::ownerDir(subject.id()),
564 BEAST_EXPECT(res2);
565 env(jv, Ter(tecDIR_FULL));
566
567 // End test
568 env.close();
569 }
570
571 {
572 testcase("Credentials fail, subject is a pseudo-account.");
573 Vault const vault{env};
574 auto [tx, keylet] = vault.create({.owner = subject, .asset = xrpIssue()});
575 env(tx);
576 env.close();
577
578 auto const sleVault = env.le(keylet);
579 if (!BEAST_EXPECT(sleVault))
580 return;
581 Account const vaultPseudo{"vault", sleVault->at(sfAccount)};
582 auto const expectedResult =
583 features[fixCleanup3_3_0] ? Ter(tecPSEUDO_ACCOUNT) : Ter(tesSUCCESS);
584
585 env(credentials::create(vaultPseudo, issuer, credType), expectedResult);
586 env.close();
587 }
588 }
589
590 {
591 Env env{*this, features};
592
593 env.fund(XRP(5000), issuer);
594 env.close();
595
596 {
597 testcase("Credentials fail, subject doesn't exist.");
598 auto const jv = credentials::create(subject, issuer, credType);
599 env(jv, Ter(tecNO_TARGET));
600 }
601 }
602
603 {
604 Env env{*this, features};
605
606 auto const reserve = drops(env.current()->fees().reserve);
607 env.fund(reserve, subject, issuer);
608 env.close();
609
610 testcase("Credentials fail, not enough reserve.");
611 {
612 auto const jv = credentials::create(subject, issuer, credType);
614 env.close();
615 }
616 }
617 }
618
619 void
621 {
622 using namespace jtx;
623
624 char const credType[] = "abcde";
625 Account const issuer{"issuer"};
626 Account const subject{"subject"};
627 Account const other{"other"};
628
629 {
630 Env env{*this, features};
631
632 env.fund(XRP(5000), subject, issuer);
633
634 {
635 testcase("CredentialsAccept fail, Credential doesn't exist.");
636 env(credentials::accept(subject, issuer, credType), Ter(tecNO_ENTRY));
637 env.close();
638 }
639
640 {
641 testcase("CredentialsAccept fail, invalid Issuer account.");
642 auto jv = credentials::accept(subject, issuer, credType);
643 jv[jss::Issuer] = to_string(xrpAccount());
644 env(jv, Ter(temINVALID_ACCOUNT_ID));
645 env.close();
646 }
647
648 {
649 testcase("CredentialsAccept fail, invalid credentialType param.");
650 auto jv = credentials::accept(subject, issuer, "");
651 env(jv, Ter(temMALFORMED));
652 }
653 }
654
655 {
656 Env env{*this, features};
657
658 env.fund(drops(env.current()->fees().accountReserve(1, 1)), issuer);
659 env.fund(drops(env.current()->fees().accountReserve(0, 1)), subject);
660 env.close();
661
662 {
663 testcase("CredentialsAccept fail, not enough reserve.");
664 env(credentials::create(subject, issuer, credType));
665 env.close();
666
667 env(credentials::accept(subject, issuer, credType), Ter(tecINSUFFICIENT_RESERVE));
668 env.close();
669
670 // check credential still present
671 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
672 BEAST_EXPECT(
673 jle.isObject() && jle.isMember(jss::result) &&
674 !jle[jss::result].isMember(jss::error) &&
675 jle[jss::result].isMember(jss::node) &&
676 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
677 jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential &&
678 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
679 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
680 jle[jss::result][jss::node]["CredentialType"] ==
681 strHex(std::string_view(credType)));
682 }
683 }
684
685 {
686 using namespace jtx;
687 Env env{*this, features};
688
689 env.fund(XRP(5000), subject, issuer);
690 env.close();
691
692 {
693 env(credentials::create(subject, issuer, credType));
694 env.close();
695
696 testcase("CredentialsAccept fail, invalid fee.");
697 auto jv = credentials::accept(subject, issuer, credType);
698 jv[jss::Fee] = -1;
699 env(jv, Ter(temBAD_FEE));
700
701 testcase("CredentialsAccept fail, lsfAccepted already set.");
702 env(credentials::accept(subject, issuer, credType));
703 env.close();
704 env(credentials::accept(subject, issuer, credType), Ter(tecDUPLICATE));
705 env.close();
706
707 // check credential still present
708 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
709 BEAST_EXPECT(
710 jle.isObject() && jle.isMember(jss::result) &&
711 !jle[jss::result].isMember(jss::error) &&
712 jle[jss::result].isMember(jss::node) &&
713 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
714 jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential &&
715 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
716 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
717 jle[jss::result][jss::node]["CredentialType"] ==
718 strHex(std::string_view(credType)));
719 }
720
721 {
722 char const credType2[] = "efghi";
723
724 testcase("CredentialsAccept fail, expired credentials.");
725 auto jv = credentials::create(subject, issuer, credType2);
726 uint32_t const t =
727 env.current()->header().parentCloseTime.time_since_epoch().count();
728 jv[sfExpiration.jsonName] = t;
729 env(jv);
730 env.close();
731
732 // credentials are expired now
733 env(credentials::accept(subject, issuer, credType2), Ter(tecEXPIRED));
734 env.close();
735
736 // check that expired credentials were deleted
737 auto const jDelCred = credentials::ledgerEntry(env, subject, issuer, credType2);
738 BEAST_EXPECT(
739 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
740 jDelCred[jss::result].isMember(jss::error) &&
741 jDelCred[jss::result][jss::error] == "entryNotFound");
742
743 BEAST_EXPECT(ownerCount(env, issuer) == 0);
744 BEAST_EXPECT(ownerCount(env, subject) == 1);
745 }
746 }
747
748 {
749 using namespace jtx;
750 Env env{*this, features};
751
752 env.fund(XRP(5000), issuer, subject, other);
753 env.close();
754
755 {
756 testcase("CredentialsAccept fail, issuer doesn't exist.");
757 auto jv = credentials::create(subject, issuer, credType);
758 env(jv);
759 env.close();
760
761 // delete issuer
762 int const delta = env.seq(issuer) + 255;
763 for (int i = 0; i < delta; ++i)
764 env.close();
765 auto const acctDelFee{drops(env.current()->fees().increment)};
766 env(acctdelete(issuer, other), Fee(acctDelFee));
767
768 // can't accept - no issuer account
769 jv = credentials::accept(subject, issuer, credType);
770 env(jv, Ter(tecNO_ISSUER));
771 env.close();
772
773 // check that expired credentials were deleted
774 auto const jDelCred = credentials::ledgerEntry(env, subject, issuer, credType);
775 BEAST_EXPECT(
776 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
777 jDelCred[jss::result].isMember(jss::error) &&
778 jDelCred[jss::result][jss::error] == "entryNotFound");
779 }
780 }
781 }
782
783 void
785 {
786 using namespace test::jtx;
787
788 char const credType[] = "abcde";
789 Account const issuer{"issuer"};
790 Account const subject{"subject"};
791 Account const other{"other"};
792
793 {
794 using namespace jtx;
795 Env env{*this, features};
796
797 env.fund(XRP(5000), subject, issuer, other);
798 env.close();
799
800 {
801 testcase("CredentialsDelete fail, no Credentials.");
802 env(credentials::deleteCred(subject, subject, issuer, credType), Ter(tecNO_ENTRY));
803 env.close();
804 }
805
806 {
807 testcase("CredentialsDelete fail, invalid Subject account.");
808 auto jv = credentials::deleteCred(subject, subject, issuer, credType);
809 jv[jss::Subject] = to_string(xrpAccount());
810 env(jv, Ter(temINVALID_ACCOUNT_ID));
811 env.close();
812 }
813
814 {
815 testcase("CredentialsDelete fail, invalid Issuer account.");
816 auto jv = credentials::deleteCred(subject, subject, issuer, credType);
817 jv[jss::Issuer] = to_string(xrpAccount());
818 env(jv, Ter(temINVALID_ACCOUNT_ID));
819 env.close();
820 }
821
822 {
823 testcase("CredentialsDelete fail, invalid credentialType param.");
824 auto jv = credentials::deleteCred(subject, subject, issuer, "");
825 env(jv, Ter(temMALFORMED));
826 }
827
828 {
829 char const credType2[] = "fghij";
830
831 env(credentials::create(subject, issuer, credType2));
832 env.close();
833
834 // Other account can't delete credentials without expiration
835 env(credentials::deleteCred(other, subject, issuer, credType2),
837 env.close();
838
839 // check credential still present
840 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType2);
841 BEAST_EXPECT(
842 jle.isObject() && jle.isMember(jss::result) &&
843 !jle[jss::result].isMember(jss::error) &&
844 jle[jss::result].isMember(jss::node) &&
845 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
846 jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential &&
847 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
848 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
849 jle[jss::result][jss::node]["CredentialType"] ==
850 strHex(std::string_view(credType2)));
851 }
852
853 {
854 testcase("CredentialsDelete fail, time not expired yet.");
855
856 auto jv = credentials::create(subject, issuer, credType);
857 // current time in XRPL epoch + 1000s
858 uint32_t const t =
859 env.current()->header().parentCloseTime.time_since_epoch().count() + 1000;
860 jv[sfExpiration.jsonName] = t;
861 env(jv);
862 env.close();
863
864 // Other account can't delete credentials that not expired
865 env(credentials::deleteCred(other, subject, issuer, credType),
867 env.close();
868
869 // check credential still present
870 auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
871 BEAST_EXPECT(
872 jle.isObject() && jle.isMember(jss::result) &&
873 !jle[jss::result].isMember(jss::error) &&
874 jle[jss::result].isMember(jss::node) &&
875 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
876 jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential &&
877 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
878 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
879 jle[jss::result][jss::node]["CredentialType"] ==
880 strHex(std::string_view(credType)));
881 }
882
883 {
884 testcase("CredentialsDelete fail, no Issuer and Subject.");
885
886 auto jv = credentials::deleteCred(subject, subject, issuer, credType);
887 jv.removeMember(jss::Subject);
888 jv.removeMember(jss::Issuer);
889 env(jv, Ter(temMALFORMED));
890 env.close();
891 }
892
893 {
894 testcase("CredentialsDelete fail, invalid fee.");
895
896 auto jv = credentials::deleteCred(subject, subject, issuer, credType);
897 jv[jss::Fee] = -1;
898 env(jv, Ter(temBAD_FEE));
899 env.close();
900 }
901
902 {
903 testcase("deleteSLE fail, bad SLE.");
904 auto view =
906 auto ter = xrpl::credentials::deleteSLE(*view, {}, env.journal);
907 BEAST_EXPECT(ter == tecNO_ENTRY);
908 }
909 }
910 }
911
912 void
914 {
915 using namespace test::jtx;
916
917 char const credType[] = "abcde";
918 Account const issuer{"issuer"};
919 Account const subject{"subject"};
920
921 {
922 using namespace jtx;
923 Env env{*this, features};
924
925 env.fund(XRP(5000), subject, issuer);
926 env.close();
927
928 {
929 testcase("Credentials fail, Feature is not enabled.");
930 env(credentials::create(subject, issuer, credType), Ter(temDISABLED));
931 env(credentials::accept(subject, issuer, credType), Ter(temDISABLED));
932 env(credentials::deleteCred(subject, subject, issuer, credType), Ter(temDISABLED));
933 }
934 }
935 }
936
937 void
939 {
940 using namespace test::jtx;
941
942 char const credType[] = "abcde";
943 Account const issuer{"issuer"};
944 Account const subject{"subject"};
945
946 {
947 using namespace jtx;
948 Env env{*this};
949
950 env.fund(XRP(5000), subject, issuer);
951 env.close();
952
953 env(credentials::create(subject, issuer, credType));
954 env.close();
955
956 env(credentials::accept(subject, issuer, credType));
957 env.close();
958
959 testcase("account_tx");
960
961 std::string txHash0, txHash1;
962 {
963 json::Value params;
964 params[jss::account] = subject.human();
965 auto const jv = env.rpc("json", "account_tx", to_string(params))[jss::result];
966
967 BEAST_EXPECT(jv[jss::transactions].size() == 4);
968 auto const& tx0(jv[jss::transactions][0u][jss::tx]);
969 BEAST_EXPECT(tx0[jss::TransactionType] == jss::CredentialAccept);
970 auto const& tx1(jv[jss::transactions][1u][jss::tx]);
971 BEAST_EXPECT(tx1[jss::TransactionType] == jss::CredentialCreate);
972 txHash0 = tx0[jss::hash].asString();
973 txHash1 = tx1[jss::hash].asString();
974 }
975
976 {
977 json::Value params;
978 params[jss::account] = issuer.human();
979 auto const jv = env.rpc("json", "account_tx", to_string(params))[jss::result];
980
981 BEAST_EXPECT(jv[jss::transactions].size() == 4);
982 auto const& tx0(jv[jss::transactions][0u][jss::tx]);
983 BEAST_EXPECT(tx0[jss::TransactionType] == jss::CredentialAccept);
984 auto const& tx1(jv[jss::transactions][1u][jss::tx]);
985 BEAST_EXPECT(tx1[jss::TransactionType] == jss::CredentialCreate);
986
987 BEAST_EXPECT(txHash0 == tx0[jss::hash].asString());
988 BEAST_EXPECT(txHash1 == tx1[jss::hash].asString());
989 }
990
991 testcase("account_objects");
992 std::string objectIdx;
993 {
994 json::Value params;
995 params[jss::account] = subject.human();
996 auto jv = env.rpc("json", "account_objects", to_string(params))[jss::result];
997
998 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
999 auto const& object(jv[jss::account_objects][0u]);
1000
1001 BEAST_EXPECT(object["LedgerEntryType"].asString() == jss::Credential);
1002 objectIdx = object[jss::index].asString();
1003 }
1004
1005 {
1006 json::Value params;
1007 params[jss::account] = issuer.human();
1008 auto jv = env.rpc("json", "account_objects", to_string(params))[jss::result];
1009
1010 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
1011 auto const& object(jv[jss::account_objects][0u]);
1012
1013 BEAST_EXPECT(object["LedgerEntryType"].asString() == jss::Credential);
1014 BEAST_EXPECT(objectIdx == object[jss::index].asString());
1015 }
1016 }
1017 }
1018
1019 void
1021 {
1022 using namespace test::jtx;
1023
1024 bool const enabled = features[fixInvalidTxFlags];
1025 testcase(std::string("Test flag, fix ") + (enabled ? "enabled" : "disabled"));
1026
1027 char const credType[] = "abcde";
1028 Account const issuer{"issuer"};
1029 Account const subject{"subject"};
1030
1031 {
1032 using namespace jtx;
1033 Env env{*this, features};
1034
1035 env.fund(XRP(5000), subject, issuer);
1036 env.close();
1037
1038 {
1039 Ter const expected(enabled ? TER(temINVALID_FLAG) : TER(tesSUCCESS));
1040 env(credentials::create(subject, issuer, credType),
1041 Txflags(tfTransferable),
1042 expected);
1043 env(credentials::accept(subject, issuer, credType),
1044 Txflags(tfSellNFToken),
1045 expected);
1046 env(credentials::deleteCred(subject, subject, issuer, credType),
1047 Txflags(tfPassive),
1048 expected);
1049 }
1050 }
1051 }
1052
1053 void
1055 {
1056 bool const fixEnabled = features[fixCleanup3_1_3];
1057 testcase(
1058 "removeExpired ignores deleteSLE failure " +
1059 (fixEnabled ? std::string(" after fix") : std::string(" before fix")));
1060
1061 using namespace test::jtx;
1062
1063 char const credType[] = "abcde";
1064 Account const issuer{"issuer"};
1065 Account const subject{"subject"};
1066 Account const becky{"becky"};
1067
1068 Env env{*this, features};
1069 env.fund(XRP(10000), issuer, subject, becky);
1070 env.close();
1071
1072 // Create credential with short expiration
1073 auto jv = credentials::create(subject, issuer, credType);
1074 uint32_t const expiration =
1075 env.current()->header().parentCloseTime.time_since_epoch().count() + 40;
1076 jv[sfExpiration.jsonName] = expiration;
1077 env(jv);
1078 env.close();
1079
1080 auto const credLE = credentials::ledgerEntry(env, subject, issuer, credType);
1081 std::string const credIdx = credLE[jss::result][jss::index].asString();
1082
1083 // Subject accepts the credential
1084 env(credentials::accept(subject, issuer, credType));
1085 env.close();
1086
1087 // Build the credential keylet
1088 auto const credKeylet =
1089 keylet::credential(subject.id(), issuer.id(), Slice(credType, std::strlen(credType)));
1090
1091 // Verify credential exists and is accepted
1092 {
1093 auto const sleCred = env.current()->read(credKeylet);
1094 BEAST_EXPECT(sleCred && sleCred->isFlag(lsfAccepted));
1095 }
1096
1097 // Create DepositPreauth
1098 env(deposit::authCredentials(becky, {{.issuer = subject, .credType = credType}}));
1099 env.close();
1100 // env();
1101 auto jtx = env.jt(pay(subject, becky, XRP(100)), credentials::Ids({credIdx}));
1102 if (!BEAST_EXPECT(jtx.stx))
1103 return;
1104 auto const stx = std::make_shared<STTx>(*jtx.stx);
1105
1106 // Create PermissionedDomain
1107 env(pdomain::setTx(becky, {{.issuer = issuer, .credType = credType}}));
1108 env.close();
1109 auto const objects = pdomain::getObjects(becky, env);
1110 if (!BEAST_EXPECT(!objects.empty()))
1111 return;
1112 auto const domain = objects.begin()->first;
1113
1114 using namespace std::chrono_literals;
1115 env.close(50s);
1116
1117 // Verify time has advanced past expiration
1118 {
1119 auto const sleCred = env.current()->read(credKeylet);
1120 BEAST_EXPECT(
1121 sleCred &&
1122 xrpl::credentials::checkExpired(*sleCred, env.current()->header().parentCloseTime));
1123 }
1124
1125 // Create an ApplyViewImpl on top of the current closed ledger
1126 // and corrupt it by erasing the issuer's account SLE
1127 auto const open = env.current();
1128 ApplyViewImpl av(&*open, TapNone);
1129
1130 // Erase the issuer's account to simulate ledger corruption
1131 auto sleIssuer = av.peek(keylet::account(issuer.id()));
1132 if (!BEAST_EXPECT(sleIssuer))
1133 return;
1134 av.erase(sleIssuer);
1135 BEAST_EXPECT(!av.exists(keylet::account(issuer.id())));
1136
1137 // Credential still exists before removeExpired
1138 BEAST_EXPECT(av.exists(credKeylet));
1139
1140 // Call removeExpired on the corrupted view
1141 STVector256 credHashes;
1142 credHashes.pushBack(credKeylet.key);
1144
1145 auto const dpTer = xrpl::verifyDepositPreauth(*stx, av, subject, becky, {}, j);
1146 auto sleCredAfter = av.read(credKeylet);
1147 BEAST_EXPECT(sleCredAfter && sleCredAfter->isFlag(lsfAccepted));
1148
1149 auto const domTer = xrpl::verifyValidDomain(av, subject.id(), domain, j);
1150 sleCredAfter = av.read(credKeylet);
1151 BEAST_EXPECT(sleCredAfter && sleCredAfter->isFlag(lsfAccepted));
1152
1153 if (fixEnabled)
1154 {
1155 // removeExpired returns error, cred wasn't deleted
1156 BEAST_EXPECT(dpTer == tecINTERNAL);
1157 BEAST_EXPECT(domTer == tecINTERNAL);
1158 }
1159 else
1160 {
1161 // removeExpired returns true (claims it found & deleted expired
1162 // creds)
1163 BEAST_EXPECT(dpTer == tecEXPIRED);
1164 BEAST_EXPECT(isTesSuccess(domTer));
1165 }
1166 }
1167
1168 void
1169 run() override
1170 {
1171 using namespace test::jtx;
1172 FeatureBitset const all{testableAmendments()};
1173 testSuccessful(all);
1175 testCreateFailed(all);
1176 testCreateFailed(all - fixDirectoryLimit);
1177 testCreateFailed(all - fixCleanup3_3_0);
1178 testAcceptFailed(all);
1179 testDeleteFailed(all);
1180 testFeatureFailed(all - featureCredentials);
1181 testFlags(all - fixInvalidTxFlags);
1182 testFlags(all);
1183 testRPC();
1184
1185 testRemoveExpiredCorruption(all - fixCleanup3_1_3);
1186 testRemoveExpiredCorruption(all | fixCleanup3_1_3);
1187 }
1188};
1189
1190BEAST_DEFINE_TESTSUITE(Credentials, app, xrpl);
1191
1192} // namespace xrpl::test
A generic endpoint for log messages.
Definition Journal.h:44
static Sink & getNullSink()
Returns a Sink which does nothing.
A testsuite class.
Definition suite.h:52
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Represents a JSON value.
Definition json_value.h:117
Editable, discardable view that can build metadata for one tx.
void pushBack(uint256 const &v)
An immutable linear range of bytes.
Definition Slice.h:28
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
SLE::const_pointer read(Keylet const &k) const override
Return the state item associated with a key.
void erase(SLE::ref sle) override
Remove a peeked SLE.
bool exists(Keylet const &k) const override
Determine if a state item exists.
Immutable cryptographic account descriptor.
Definition jtx/Account.h:21
std::string const & human() const
Returns the human readable public key.
AccountID id() const
Returns the Account ID.
A transaction testing environment.
Definition Env.h:161
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
SLE::const_pointer le(Account const &account) const
Return an account root.
Definition Env.cpp:311
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:323
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:302
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:1056
JTx jt(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
Definition Env.h:721
beast::Journal const journal
Definition Env.h:204
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:377
Set the fee on a JTx.
Definition fee.h:20
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:18
Set the flags on a JTx.
Definition txflags.h:14
Set a ticket sequence on a JTx.
Definition ticket.h:36
T make_shared(T... args)
TER deleteSLE(ApplyView &view, SLE::ref sleCredential, beast::Journal j)
bool checkExpired(SLE const &sleCredential, NetClock::time_point const &closed)
Keylet computation functions.
Definition Indexes.h:40
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:373
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
Definition Indexes.cpp:555
json::Value deleteCred(jtx::Account const &acc, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:40
json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:29
json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:16
json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:56
Keylet keylet(test::jtx::Account const &subject, test::jtx::Account const &issuer, std::string_view credType)
Definition credentials.h:21
json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:38
auto bumpLastPage(Env &env, std::uint64_t newLastPage, Keylet directory, std::function< bool(ApplyView &, uint256, std::uint64_t)> adjust) -> std::expected< void, Error >
Move the position of the last page in the user's directory on open ledger to newLastPage.
Definition directory.cpp:27
auto maximumPageIndex(Env const &env) -> std::uint64_t
Definition directory.h:57
bool adjustOwnerNode(ApplyView &view, uint256 key, std::uint64_t page)
Implementation of adjust for the most common ledger entry, i.e.
std::map< uint256, json::Value > getObjects(Account const &account, Env &env, bool withType)
json::Value setTx(AccountID const &account, Credentials const &credentials, std::optional< uint256 > domain)
json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:16
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
json::Value noop(Account const &account)
The null transaction.
Definition noop.h:14
std::uint32_t ownerCount(Env const &env, Account const &account)
FeatureBitset testableAmendments()
Definition Env.h:92
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)
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:108
constexpr std::size_t kMaxCredentialTypeLength
The maximum length of a CredentialType inside a Credential.
Definition Protocol.h:275
TER verifyValidDomain(ApplyView &view, AccountID const &account, uint256 domainID, beast::Journal j)
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:13
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
void open(soci::session &s, BasicConfig const &config, std::string const &dbName)
Open a soci session.
Definition SociDB.cpp:90
@ TapNone
Definition ApplyView.h:28
constexpr std::size_t kMaxCredentialUriLength
The maximum length of a URI inside a Credential.
Definition Protocol.h:270
@ temBAD_FEE
Definition TER.h:80
@ temINVALID_FLAG
Definition TER.h:99
@ temMALFORMED
Definition TER.h:75
@ temINVALID_ACCOUNT_ID
Definition TER.h:107
@ temDISABLED
Definition TER.h:102
bool isTesSuccess(TER x) noexcept
Definition TER.h:676
TERSubset< CanCvtToTER > TER
Definition TER.h:647
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecDIR_FULL
Definition TER.h:290
@ tecPSEUDO_ACCOUNT
Definition TER.h:365
@ tecNO_ENTRY
Definition TER.h:309
@ tecNO_TARGET
Definition TER.h:307
@ tecINTERNAL
Definition TER.h:313
@ tecEXPIRED
Definition TER.h:317
@ tecINSUFFICIENT_RESERVE
Definition TER.h:310
@ tecNO_PERMISSION
Definition TER.h:308
@ tecNO_ISSUER
Definition TER.h:302
@ tecDUPLICATE
Definition TER.h:318
@ tesSUCCESS
Definition TER.h:245
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, SLE::const_ref sleDst, beast::Journal j)
T size(T... args)
T strlen(T... args)
void run() override
Runs the suite.
void testDeleteFailed(FeatureBitset features)
void testCredentialsDelete(FeatureBitset features)
void testRemoveExpiredCorruption(FeatureBitset features)
void testCreateFailed(FeatureBitset features)
void testAcceptFailed(FeatureBitset features)
void testFlags(FeatureBitset features)
void testFeatureFailed(FeatureBitset features)
void testSuccessful(FeatureBitset features)