22#include <xrpld/app/misc/CredentialHelpers.h>
23#include <xrpld/ledger/ApplyViewImpl.h>
25#include <xrpl/basics/strHex.h>
26#include <xrpl/protocol/Feature.h>
27#include <xrpl/protocol/Indexes.h>
28#include <xrpl/protocol/Protocol.h>
29#include <xrpl/protocol/TxFlags.h>
30#include <xrpl/protocol/jss.h>
43 return strHex(expected) ==
strHex(sle->getFieldVL(field));
51 using namespace test::jtx;
53 char const credType[] =
"abcde";
54 char const uri[] =
"uri";
57 Account const subject{
"subject"};
60 Env env{*
this, features};
67 env.fund(
XRP(5000), subject, issuer, other);
75 auto const sleCred = env.le(credKey);
76 BEAST_EXPECT(
static_cast<bool>(sleCred));
80 BEAST_EXPECT(sleCred->getAccountID(sfSubject) == subject.id());
81 BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id());
82 BEAST_EXPECT(!sleCred->getFieldU32(sfFlags));
85 BEAST_EXPECT(
checkVL(sleCred, sfCredentialType, credType));
86 BEAST_EXPECT(
checkVL(sleCred, sfURI, uri));
90 jle.isObject() && jle.isMember(jss::result) &&
91 !jle[jss::result].isMember(jss::error) &&
92 jle[jss::result].isMember(jss::node) &&
93 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
94 jle[jss::result][jss::node][
"LedgerEntryType"] ==
96 jle[jss::result][jss::node][jss::Issuer] ==
98 jle[jss::result][jss::node][jss::Subject] ==
100 jle[jss::result][jss::node][
"CredentialType"] ==
109 auto const sleCred = env.le(credKey);
110 BEAST_EXPECT(
static_cast<bool>(sleCred));
114 BEAST_EXPECT(sleCred->getAccountID(sfSubject) == subject.id());
115 BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id());
118 BEAST_EXPECT(
checkVL(sleCred, sfCredentialType, credType));
119 BEAST_EXPECT(
checkVL(sleCred, sfURI, uri));
120 BEAST_EXPECT(sleCred->getFieldU32(sfFlags) ==
lsfAccepted);
126 BEAST_EXPECT(!env.le(credKey));
134 jle.isObject() && jle.isMember(jss::result) &&
135 jle[jss::result].isMember(jss::error) &&
136 jle[jss::result][jss::error] ==
"entryNotFound");
149 auto const sleCred = env.le(credKey);
150 BEAST_EXPECT(
static_cast<bool>(sleCred));
154 BEAST_EXPECT(sleCred->getAccountID(sfSubject) == issuer.id());
155 BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id());
156 BEAST_EXPECT((sleCred->getFieldU32(sfFlags) &
lsfAccepted));
158 sleCred->getFieldU64(sfIssuerNode) ==
159 sleCred->getFieldU64(sfSubjectNode));
161 BEAST_EXPECT(
checkVL(sleCred, sfCredentialType, credType));
162 BEAST_EXPECT(
checkVL(sleCred, sfURI, uri));
166 jle.isObject() && jle.isMember(jss::result) &&
167 !jle[jss::result].isMember(jss::error) &&
168 jle[jss::result].isMember(jss::node) &&
169 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
170 jle[jss::result][jss::node][
"LedgerEntryType"] ==
172 jle[jss::result][jss::node][jss::Issuer] ==
174 jle[jss::result][jss::node][jss::Subject] ==
176 jle[jss::result][jss::node][
"CredentialType"] ==
183 BEAST_EXPECT(!env.le(credKey));
190 jle.isObject() && jle.isMember(jss::result) &&
191 jle[jss::result].isMember(jss::error) &&
192 jle[jss::result][jss::error] ==
"entryNotFound");
200 using namespace test::jtx;
202 char const credType[] =
"abcde";
204 Account const issuer{
"issuer"};
205 Account const subject{
"subject"};
208 Env env{*
this, features};
211 env.fund(
XRP(5000), issuer, subject, other);
215 testcase(
"Delete issuer before accept");
223 int const delta = env.seq(issuer) + 255;
224 for (
int i = 0; i < delta; ++i)
226 auto const acctDelFee{
drops(env.current()->fees().increment)};
233 BEAST_EXPECT(!env.le(credKey));
240 jle.isObject() && jle.isMember(jss::result) &&
241 jle[jss::result].isMember(jss::error) &&
242 jle[jss::result][jss::error] ==
"entryNotFound");
246 env.fund(
XRP(5000), issuer);
251 testcase(
"Delete issuer after accept");
261 int const delta = env.seq(issuer) + 255;
262 for (
int i = 0; i < delta; ++i)
264 auto const acctDelFee{
drops(env.current()->fees().increment)};
271 BEAST_EXPECT(!env.le(credKey));
278 jle.isObject() && jle.isMember(jss::result) &&
279 jle[jss::result].isMember(jss::error) &&
280 jle[jss::result][jss::error] ==
"entryNotFound");
284 env.fund(
XRP(5000), issuer);
289 testcase(
"Delete subject before accept");
297 int const delta = env.seq(subject) + 255;
298 for (
int i = 0; i < delta; ++i)
300 auto const acctDelFee{
drops(env.current()->fees().increment)};
307 BEAST_EXPECT(!env.le(credKey));
314 jle.isObject() && jle.isMember(jss::result) &&
315 jle[jss::result].isMember(jss::error) &&
316 jle[jss::result][jss::error] ==
"entryNotFound");
320 env.fund(
XRP(5000), subject);
325 testcase(
"Delete subject after accept");
335 int const delta = env.seq(subject) + 255;
336 for (
int i = 0; i < delta; ++i)
338 auto const acctDelFee{
drops(env.current()->fees().increment)};
345 BEAST_EXPECT(!env.le(credKey));
352 jle.isObject() && jle.isMember(jss::result) &&
353 jle[jss::result].isMember(jss::error) &&
354 jle[jss::result][jss::error] ==
"entryNotFound");
358 env.fund(
XRP(5000), subject);
367 uint32_t
const t = env.current()
369 .parentCloseTime.time_since_epoch()
371 jv[sfExpiration.jsonName] = t + 20;
385 BEAST_EXPECT(!env.le(credKey));
393 jle.isObject() && jle.isMember(jss::result) &&
394 jle[jss::result].isMember(jss::error) &&
395 jle[jss::result][jss::error] ==
"entryNotFound");
411 BEAST_EXPECT(!env.le(credKey));
417 jle.isObject() && jle.isMember(jss::result) &&
418 jle[jss::result].isMember(jss::error) &&
419 jle[jss::result][jss::error] ==
"entryNotFound");
433 BEAST_EXPECT(!env.le(credKey));
439 jle.isObject() && jle.isMember(jss::result) &&
440 jle[jss::result].isMember(jss::error) &&
441 jle[jss::result][jss::error] ==
"entryNotFound");
449 using namespace test::jtx;
451 char const credType[] =
"abcde";
453 Account const issuer{
"issuer"};
454 Account const subject{
"subject"};
458 Env env{*
this, features};
460 env.fund(
XRP(5000), subject, issuer);
464 testcase(
"Credentials fail, no subject param.");
466 jv.removeMember(jss::Subject);
477 testcase(
"Credentials fail, no credentialType param.");
479 jv.removeMember(sfCredentialType.jsonName);
484 testcase(
"Credentials fail, empty credentialType param.");
491 "Credentials fail, credentialType length > "
492 "maxCredentialTypeLength.");
494 "abcdefghijklmnopqrstuvwxyz01234567890qwertyuiop[]"
495 "asdfghjkl;'zxcvbnm8237tr28weufwldebvfv8734t07p";
502 testcase(
"Credentials fail, URI length > 256.");
504 "abcdefghijklmnopqrstuvwxyz01234567890qwertyuiop[]"
505 "asdfghjkl;'zxcvbnm8237tr28weufwldebvfv8734t07p "
506 "9hfup;wDJFBVSD8f72 "
508 "djvbldafghwpEFHdjfaidfgio84763tfysgdvhjasbd "
509 "vujhgWQIE7F6WEUYFGWUKEYFVQW87FGWOEFWEFUYWVEF8723GFWEFB"
511 "fv28o37gfwEFB3872TFO8GSDSDVD";
519 testcase(
"Credentials fail, URI empty.");
526 testcase(
"Credentials fail, expiration in the past.");
529 uint32_t
const t = env.current()
531 .parentCloseTime.time_since_epoch()
534 jv[sfExpiration.jsonName] = t;
539 testcase(
"Credentials fail, invalid fee.");
547 testcase(
"Credentials fail, duplicate.");
558 jle.isObject() && jle.isMember(jss::result) &&
559 !jle[jss::result].isMember(jss::error) &&
560 jle[jss::result].isMember(jss::node) &&
561 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
562 jle[jss::result][jss::node][
"LedgerEntryType"] ==
564 jle[jss::result][jss::node][jss::Issuer] ==
566 jle[jss::result][jss::node][jss::Subject] ==
568 jle[jss::result][jss::node][
"CredentialType"] ==
575 Env env{*
this, features};
577 env.fund(
XRP(5000), issuer);
581 testcase(
"Credentials fail, subject doesn't exist.");
589 Env env{*
this, features};
591 auto const reserve =
drops(env.current()->fees().accountReserve(0));
592 env.fund(
reserve, subject, issuer);
595 testcase(
"Credentials fail, not enough reserve.");
609 char const credType[] =
"abcde";
610 Account const issuer{
"issuer"};
611 Account const subject{
"subject"};
615 Env env{*
this, features};
617 env.fund(
XRP(5000), subject, issuer);
620 testcase(
"CredentialsAccept fail, Credential doesn't exist.");
627 testcase(
"CredentialsAccept fail, invalid Issuer account.");
636 "CredentialsAccept fail, invalid credentialType param.");
643 Env env{*
this, features};
645 env.fund(
drops(env.current()->fees().accountReserve(1)), issuer);
646 env.fund(
drops(env.current()->fees().accountReserve(0)), subject);
650 testcase(
"CredentialsAccept fail, not enough reserve.");
662 jle.isObject() && jle.isMember(jss::result) &&
663 !jle[jss::result].isMember(jss::error) &&
664 jle[jss::result].isMember(jss::node) &&
665 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
666 jle[jss::result][jss::node][
"LedgerEntryType"] ==
668 jle[jss::result][jss::node][jss::Issuer] ==
670 jle[jss::result][jss::node][jss::Subject] ==
672 jle[jss::result][jss::node][
"CredentialType"] ==
679 Env env{*
this, features};
681 env.fund(
XRP(5000), subject, issuer);
688 testcase(
"CredentialsAccept fail, invalid fee.");
693 testcase(
"CredentialsAccept fail, lsfAccepted already set.");
704 jle.isObject() && jle.isMember(jss::result) &&
705 !jle[jss::result].isMember(jss::error) &&
706 jle[jss::result].isMember(jss::node) &&
707 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
708 jle[jss::result][jss::node][
"LedgerEntryType"] ==
710 jle[jss::result][jss::node][jss::Issuer] ==
712 jle[jss::result][jss::node][jss::Subject] ==
714 jle[jss::result][jss::node][
"CredentialType"] ==
719 char const credType2[] =
"efghi";
721 testcase(
"CredentialsAccept fail, expired credentials.");
723 uint32_t
const t = env.current()
725 .parentCloseTime.time_since_epoch()
727 jv[sfExpiration.jsonName] = t;
737 auto const jDelCred =
740 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
741 jDelCred[jss::result].isMember(jss::error) &&
742 jDelCred[jss::result][jss::error] ==
"entryNotFound");
751 Env env{*
this, features};
753 env.fund(
XRP(5000), issuer, subject, other);
757 testcase(
"CredentialsAccept fail, issuer doesn't exist.");
763 int const delta = env.seq(issuer) + 255;
764 for (
int i = 0; i < delta; ++i)
766 auto const acctDelFee{
drops(env.current()->fees().increment)};
775 auto const jDelCred =
778 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
779 jDelCred[jss::result].isMember(jss::error) &&
780 jDelCred[jss::result][jss::error] ==
"entryNotFound");
788 using namespace test::jtx;
790 char const credType[] =
"abcde";
791 Account const issuer{
"issuer"};
792 Account const subject{
"subject"};
797 Env env{*
this, features};
799 env.fund(
XRP(5000), subject, issuer, other);
803 testcase(
"CredentialsDelete fail, no Credentials.");
810 testcase(
"CredentialsDelete fail, invalid Subject account.");
819 testcase(
"CredentialsDelete fail, invalid Issuer account.");
829 "CredentialsDelete fail, invalid credentialType param.");
835 char const credType2[] =
"fghij";
849 jle.isObject() && jle.isMember(jss::result) &&
850 !jle[jss::result].isMember(jss::error) &&
851 jle[jss::result].isMember(jss::node) &&
852 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
853 jle[jss::result][jss::node][
"LedgerEntryType"] ==
855 jle[jss::result][jss::node][jss::Issuer] ==
857 jle[jss::result][jss::node][jss::Subject] ==
859 jle[jss::result][jss::node][
"CredentialType"] ==
864 testcase(
"CredentialsDelete fail, time not expired yet.");
868 uint32_t
const t = env.current()
870 .parentCloseTime.time_since_epoch()
873 jv[sfExpiration.jsonName] = t;
886 jle.isObject() && jle.isMember(jss::result) &&
887 !jle[jss::result].isMember(jss::error) &&
888 jle[jss::result].isMember(jss::node) &&
889 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
890 jle[jss::result][jss::node][
"LedgerEntryType"] ==
892 jle[jss::result][jss::node][jss::Issuer] ==
894 jle[jss::result][jss::node][jss::Subject] ==
896 jle[jss::result][jss::node][
"CredentialType"] ==
901 testcase(
"CredentialsDelete fail, no Issuer and Subject.");
905 jv.removeMember(jss::Subject);
906 jv.removeMember(jss::Issuer);
912 testcase(
"CredentialsDelete fail, invalid fee.");
922 testcase(
"deleteSLE fail, bad SLE.");
923 auto view = std::make_shared<ApplyViewImpl>(
935 using namespace test::jtx;
937 char const credType[] =
"abcde";
938 Account const issuer{
"issuer"};
939 Account const subject{
"subject"};
943 Env env{*
this, features};
945 env.fund(
XRP(5000), subject, issuer);
949 testcase(
"Credentials fail, Feature is not enabled.");
963 using namespace test::jtx;
965 char const credType[] =
"abcde";
966 Account const issuer{
"issuer"};
967 Account const subject{
"subject"};
973 env.
fund(
XRP(5000), subject, issuer);
987 params[jss::account] = subject.human();
988 auto const jv = env.rpc(
989 "json",
"account_tx",
to_string(params))[jss::result];
991 BEAST_EXPECT(jv[jss::transactions].size() == 4);
992 auto const& tx0(jv[jss::transactions][0u][jss::tx]);
994 tx0[jss::TransactionType] == jss::CredentialAccept);
995 auto const& tx1(jv[jss::transactions][1u][jss::tx]);
997 tx1[jss::TransactionType] == jss::CredentialCreate);
998 txHash0 = tx0[jss::hash].asString();
999 txHash1 = tx1[jss::hash].asString();
1004 params[jss::account] = issuer.human();
1005 auto const jv = env.rpc(
1006 "json",
"account_tx",
to_string(params))[jss::result];
1008 BEAST_EXPECT(jv[jss::transactions].size() == 4);
1009 auto const& tx0(jv[jss::transactions][0u][jss::tx]);
1011 tx0[jss::TransactionType] == jss::CredentialAccept);
1012 auto const& tx1(jv[jss::transactions][1u][jss::tx]);
1014 tx1[jss::TransactionType] == jss::CredentialCreate);
1016 BEAST_EXPECT(txHash0 == tx0[jss::hash].asString());
1017 BEAST_EXPECT(txHash1 == tx1[jss::hash].asString());
1024 params[jss::account] = subject.human();
1026 "json",
"account_objects",
to_string(params))[jss::result];
1028 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
1029 auto const& object(jv[jss::account_objects][0u]);
1032 object[
"LedgerEntryType"].asString() == jss::Credential);
1033 objectIdx =
object[jss::index].asString();
1038 params[jss::account] = issuer.human();
1040 "json",
"account_objects",
to_string(params))[jss::result];
1042 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
1043 auto const& object(jv[jss::account_objects][0u]);
1046 object[
"LedgerEntryType"].asString() == jss::Credential);
1047 BEAST_EXPECT(objectIdx ==
object[jss::index].asString());
1055 using namespace test::jtx;
1057 bool const enabled = features[fixInvalidTxFlags];
1060 (enabled ?
"enabled" :
"disabled"));
1062 char const credType[] =
"abcde";
1063 Account const issuer{
"issuer"};
1064 Account const subject{
"subject"};
1067 using namespace jtx;
1068 Env env{*
this, features};
1070 env.fund(
XRP(5000), subject, issuer);
1092 using namespace test::jtx;
1106BEAST_DEFINE_TESTSUITE(Credentials, app,
ripple);
testcase_t testcase
Memberspace for declaring test cases.
Immutable cryptographic account descriptor.
A transaction testing environment.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Set the expected result code for a JTx The test will fail if the code doesn't match.
TER deleteSLE(ApplyView &view, std::shared_ptr< SLE > const &sleCredential, beast::Journal j)
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Keylet keylet(test::jtx::Account const &subject, test::jtx::Account const &issuer, std::string_view credType)
Json::Value deleteCred(jtx::Account const &acc, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
std::uint32_t ownerCount(Env const &env, Account const &account)
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
XRP_t const XRP
Converts to XRP Issue or STAmount.
FeatureBitset supported_amendments()
static bool checkVL(std::shared_ptr< SLE const > const &sle, SField const &field, 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.
AccountID const & xrpAccount()
Compute AccountID from public key.
std::size_t constexpr maxCredentialURILength
The maximum length of a URI inside a Credential.
constexpr std::uint32_t const tfSellNFToken
constexpr std::uint32_t tfPassive
std::string strHex(FwdIt begin, FwdIt end)
std::size_t constexpr maxCredentialTypeLength
The maximum length of a CredentialType inside a Credential.
@ tecINSUFFICIENT_RESERVE
std::string to_string(base_uint< Bits, Tag > const &a)
TERSubset< CanCvtToTER > TER
constexpr std::uint32_t const tfTransferable
void testAcceptFailed(FeatureBitset features)
void testSuccessful(FeatureBitset features)
void testDeleteFailed(FeatureBitset features)
void testFeatureFailed(FeatureBitset features)
void testFlags(FeatureBitset features)
void testCredentialsDelete(FeatureBitset features)
void run() override
Runs the suite.
void testCreateFailed(FeatureBitset features)