21#include <xrpld/app/misc/CredentialHelpers.h>
22#include <xrpld/ledger/ApplyViewImpl.h>
23#include <xrpl/basics/strHex.h>
24#include <xrpl/protocol/Feature.h>
25#include <xrpl/protocol/Indexes.h>
26#include <xrpl/protocol/Protocol.h>
27#include <xrpl/protocol/TxFlags.h>
28#include <xrpl/protocol/jss.h>
42 return strHex(expected) ==
strHex(sle->getFieldVL(field));
60 using namespace test::jtx;
62 const char credType[] =
"abcde";
63 const char uri[] =
"uri";
66 Account const subject{
"subject"};
69 Env env{*
this, features};
76 env.fund(
XRP(5000), subject, issuer, other);
84 auto const sleCred = env.le(credKey);
85 BEAST_EXPECT(
static_cast<bool>(sleCred));
89 BEAST_EXPECT(sleCred->getAccountID(sfSubject) == subject.id());
90 BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id());
91 BEAST_EXPECT(!sleCred->getFieldU32(sfFlags));
94 BEAST_EXPECT(
checkVL(sleCred, sfCredentialType, credType));
95 BEAST_EXPECT(
checkVL(sleCred, sfURI, uri));
99 jle.isObject() && jle.isMember(jss::result) &&
100 !jle[jss::result].isMember(jss::error) &&
101 jle[jss::result].isMember(jss::node) &&
102 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
103 jle[jss::result][jss::node][
"LedgerEntryType"] ==
105 jle[jss::result][jss::node][jss::Issuer] ==
107 jle[jss::result][jss::node][jss::Subject] ==
109 jle[jss::result][jss::node][
"CredentialType"] ==
118 auto const sleCred = env.le(credKey);
119 BEAST_EXPECT(
static_cast<bool>(sleCred));
123 BEAST_EXPECT(sleCred->getAccountID(sfSubject) == subject.id());
124 BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id());
127 BEAST_EXPECT(
checkVL(sleCred, sfCredentialType, credType));
128 BEAST_EXPECT(
checkVL(sleCred, sfURI, uri));
129 BEAST_EXPECT(sleCred->getFieldU32(sfFlags) ==
lsfAccepted);
135 BEAST_EXPECT(!env.le(credKey));
143 jle.isObject() && jle.isMember(jss::result) &&
144 jle[jss::result].isMember(jss::error) &&
145 jle[jss::result][jss::error] ==
"entryNotFound");
158 auto const sleCred = env.le(credKey);
159 BEAST_EXPECT(
static_cast<bool>(sleCred));
163 BEAST_EXPECT(sleCred->getAccountID(sfSubject) == issuer.id());
164 BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id());
165 BEAST_EXPECT((sleCred->getFieldU32(sfFlags) &
lsfAccepted));
167 sleCred->getFieldU64(sfIssuerNode) ==
168 sleCred->getFieldU64(sfSubjectNode));
170 BEAST_EXPECT(
checkVL(sleCred, sfCredentialType, credType));
171 BEAST_EXPECT(
checkVL(sleCred, sfURI, uri));
175 jle.isObject() && jle.isMember(jss::result) &&
176 !jle[jss::result].isMember(jss::error) &&
177 jle[jss::result].isMember(jss::node) &&
178 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
179 jle[jss::result][jss::node][
"LedgerEntryType"] ==
181 jle[jss::result][jss::node][jss::Issuer] ==
183 jle[jss::result][jss::node][jss::Subject] ==
185 jle[jss::result][jss::node][
"CredentialType"] ==
192 BEAST_EXPECT(!env.le(credKey));
199 jle.isObject() && jle.isMember(jss::result) &&
200 jle[jss::result].isMember(jss::error) &&
201 jle[jss::result][jss::error] ==
"entryNotFound");
209 using namespace test::jtx;
211 const char credType[] =
"abcde";
213 Account const issuer{
"issuer"};
214 Account const subject{
"subject"};
217 Env env{*
this, features};
220 env.fund(
XRP(5000), issuer, subject, other);
224 testcase(
"Delete issuer before accept");
232 int const delta = env.seq(issuer) + 255;
233 for (
int i = 0; i < delta; ++i)
235 auto const acctDelFee{
drops(env.current()->fees().increment)};
242 BEAST_EXPECT(!env.le(credKey));
249 jle.isObject() && jle.isMember(jss::result) &&
250 jle[jss::result].isMember(jss::error) &&
251 jle[jss::result][jss::error] ==
"entryNotFound");
255 env.fund(
XRP(5000), issuer);
260 testcase(
"Delete issuer after accept");
270 int const delta = env.seq(issuer) + 255;
271 for (
int i = 0; i < delta; ++i)
273 auto const acctDelFee{
drops(env.current()->fees().increment)};
280 BEAST_EXPECT(!env.le(credKey));
287 jle.isObject() && jle.isMember(jss::result) &&
288 jle[jss::result].isMember(jss::error) &&
289 jle[jss::result][jss::error] ==
"entryNotFound");
293 env.fund(
XRP(5000), issuer);
298 testcase(
"Delete subject before accept");
306 int const delta = env.seq(subject) + 255;
307 for (
int i = 0; i < delta; ++i)
309 auto const acctDelFee{
drops(env.current()->fees().increment)};
316 BEAST_EXPECT(!env.le(credKey));
323 jle.isObject() && jle.isMember(jss::result) &&
324 jle[jss::result].isMember(jss::error) &&
325 jle[jss::result][jss::error] ==
"entryNotFound");
329 env.fund(
XRP(5000), subject);
334 testcase(
"Delete subject after accept");
344 int const delta = env.seq(subject) + 255;
345 for (
int i = 0; i < delta; ++i)
347 auto const acctDelFee{
drops(env.current()->fees().increment)};
354 BEAST_EXPECT(!env.le(credKey));
361 jle.isObject() && jle.isMember(jss::result) &&
362 jle[jss::result].isMember(jss::error) &&
363 jle[jss::result][jss::error] ==
"entryNotFound");
367 env.fund(
XRP(5000), subject);
376 uint32_t
const t = env.current()
378 .parentCloseTime.time_since_epoch()
380 jv[sfExpiration.jsonName] = t + 20;
394 BEAST_EXPECT(!env.le(credKey));
402 jle.isObject() && jle.isMember(jss::result) &&
403 jle[jss::result].isMember(jss::error) &&
404 jle[jss::result][jss::error] ==
"entryNotFound");
420 BEAST_EXPECT(!env.le(credKey));
426 jle.isObject() && jle.isMember(jss::result) &&
427 jle[jss::result].isMember(jss::error) &&
428 jle[jss::result][jss::error] ==
"entryNotFound");
442 BEAST_EXPECT(!env.le(credKey));
448 jle.isObject() && jle.isMember(jss::result) &&
449 jle[jss::result].isMember(jss::error) &&
450 jle[jss::result][jss::error] ==
"entryNotFound");
458 using namespace test::jtx;
460 const char credType[] =
"abcde";
462 Account const issuer{
"issuer"};
463 Account const subject{
"subject"};
467 Env env{*
this, features};
469 env.fund(
XRP(5000), subject, issuer);
473 testcase(
"Credentials fail, no subject param.");
475 jv.removeMember(jss::Subject);
486 testcase(
"Credentials fail, no credentialType param.");
488 jv.removeMember(sfCredentialType.jsonName);
493 testcase(
"Credentials fail, empty credentialType param.");
500 "Credentials fail, credentialType length > "
501 "maxCredentialTypeLength.");
503 "abcdefghijklmnopqrstuvwxyz01234567890qwertyuiop[]"
504 "asdfghjkl;'zxcvbnm8237tr28weufwldebvfv8734t07p";
511 testcase(
"Credentials fail, URI length > 256.");
513 "abcdefghijklmnopqrstuvwxyz01234567890qwertyuiop[]"
514 "asdfghjkl;'zxcvbnm8237tr28weufwldebvfv8734t07p "
515 "9hfup;wDJFBVSD8f72 "
517 "djvbldafghwpEFHdjfaidfgio84763tfysgdvhjasbd "
518 "vujhgWQIE7F6WEUYFGWUKEYFVQW87FGWOEFWEFUYWVEF8723GFWEFB"
520 "fv28o37gfwEFB3872TFO8GSDSDVD";
528 testcase(
"Credentials fail, URI empty.");
535 testcase(
"Credentials fail, expiration in the past.");
538 uint32_t
const t = env.current()
540 .parentCloseTime.time_since_epoch()
543 jv[sfExpiration.jsonName] = t;
548 testcase(
"Credentials fail, invalid fee.");
556 testcase(
"Credentials fail, duplicate.");
567 jle.isObject() && jle.isMember(jss::result) &&
568 !jle[jss::result].isMember(jss::error) &&
569 jle[jss::result].isMember(jss::node) &&
570 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
571 jle[jss::result][jss::node][
"LedgerEntryType"] ==
573 jle[jss::result][jss::node][jss::Issuer] ==
575 jle[jss::result][jss::node][jss::Subject] ==
577 jle[jss::result][jss::node][
"CredentialType"] ==
584 Env env{*
this, features};
586 env.fund(
XRP(5000), issuer);
590 testcase(
"Credentials fail, subject doesn't exist.");
598 Env env{*
this, features};
600 auto const reserve =
drops(env.current()->fees().accountReserve(0));
601 env.fund(
reserve, subject, issuer);
604 testcase(
"Credentials fail, not enough reserve.");
618 const char credType[] =
"abcde";
619 Account const issuer{
"issuer"};
620 Account const subject{
"subject"};
624 Env env{*
this, features};
626 env.fund(
XRP(5000), subject, issuer);
629 testcase(
"CredentialsAccept fail, Credential doesn't exist.");
636 testcase(
"CredentialsAccept fail, invalid Issuer account.");
645 "CredentialsAccept fail, invalid credentialType param.");
652 Env env{*
this, features};
654 env.fund(
drops(env.current()->fees().accountReserve(1)), issuer);
655 env.fund(
drops(env.current()->fees().accountReserve(0)), subject);
659 testcase(
"CredentialsAccept fail, not enough reserve.");
671 jle.isObject() && jle.isMember(jss::result) &&
672 !jle[jss::result].isMember(jss::error) &&
673 jle[jss::result].isMember(jss::node) &&
674 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
675 jle[jss::result][jss::node][
"LedgerEntryType"] ==
677 jle[jss::result][jss::node][jss::Issuer] ==
679 jle[jss::result][jss::node][jss::Subject] ==
681 jle[jss::result][jss::node][
"CredentialType"] ==
688 Env env{*
this, features};
690 env.fund(
XRP(5000), subject, issuer);
697 testcase(
"CredentialsAccept fail, invalid fee.");
702 testcase(
"CredentialsAccept fail, lsfAccepted already set.");
713 jle.isObject() && jle.isMember(jss::result) &&
714 !jle[jss::result].isMember(jss::error) &&
715 jle[jss::result].isMember(jss::node) &&
716 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
717 jle[jss::result][jss::node][
"LedgerEntryType"] ==
719 jle[jss::result][jss::node][jss::Issuer] ==
721 jle[jss::result][jss::node][jss::Subject] ==
723 jle[jss::result][jss::node][
"CredentialType"] ==
728 const char credType2[] =
"efghi";
730 testcase(
"CredentialsAccept fail, expired credentials.");
732 uint32_t
const t = env.current()
734 .parentCloseTime.time_since_epoch()
736 jv[sfExpiration.jsonName] = t;
746 auto const jDelCred =
749 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
750 jDelCred[jss::result].isMember(jss::error) &&
751 jDelCred[jss::result][jss::error] ==
"entryNotFound");
760 Env env{*
this, features};
762 env.fund(
XRP(5000), issuer, subject, other);
766 testcase(
"CredentialsAccept fail, issuer doesn't exist.");
772 int const delta = env.seq(issuer) + 255;
773 for (
int i = 0; i < delta; ++i)
775 auto const acctDelFee{
drops(env.current()->fees().increment)};
784 auto const jDelCred =
787 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
788 jDelCred[jss::result].isMember(jss::error) &&
789 jDelCred[jss::result][jss::error] ==
"entryNotFound");
797 using namespace test::jtx;
799 const char credType[] =
"abcde";
800 Account const issuer{
"issuer"};
801 Account const subject{
"subject"};
806 Env env{*
this, features};
808 env.fund(
XRP(5000), subject, issuer, other);
812 testcase(
"CredentialsDelete fail, no Credentials.");
819 testcase(
"CredentialsDelete fail, invalid Subject account.");
828 testcase(
"CredentialsDelete fail, invalid Issuer account.");
838 "CredentialsDelete fail, invalid credentialType param.");
844 const char credType2[] =
"fghij";
858 jle.isObject() && jle.isMember(jss::result) &&
859 !jle[jss::result].isMember(jss::error) &&
860 jle[jss::result].isMember(jss::node) &&
861 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
862 jle[jss::result][jss::node][
"LedgerEntryType"] ==
864 jle[jss::result][jss::node][jss::Issuer] ==
866 jle[jss::result][jss::node][jss::Subject] ==
868 jle[jss::result][jss::node][
"CredentialType"] ==
873 testcase(
"CredentialsDelete fail, time not expired yet.");
877 uint32_t
const t = env.current()
879 .parentCloseTime.time_since_epoch()
882 jv[sfExpiration.jsonName] = t;
895 jle.isObject() && jle.isMember(jss::result) &&
896 !jle[jss::result].isMember(jss::error) &&
897 jle[jss::result].isMember(jss::node) &&
898 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
899 jle[jss::result][jss::node][
"LedgerEntryType"] ==
901 jle[jss::result][jss::node][jss::Issuer] ==
903 jle[jss::result][jss::node][jss::Subject] ==
905 jle[jss::result][jss::node][
"CredentialType"] ==
910 testcase(
"CredentialsDelete fail, no Issuer and Subject.");
914 jv.removeMember(jss::Subject);
915 jv.removeMember(jss::Issuer);
921 testcase(
"CredentialsDelete fail, invalid fee.");
931 testcase(
"deleteSLE fail, bad SLE.");
932 auto view = std::make_shared<ApplyViewImpl>(
944 using namespace test::jtx;
946 const char credType[] =
"abcde";
947 Account const issuer{
"issuer"};
948 Account const subject{
"subject"};
952 Env env{*
this, features};
954 env.fund(
XRP(5000), subject, issuer);
958 testcase(
"Credentials fail, Feature is not enabled.");
972 using namespace test::jtx;
974 const char credType[] =
"abcde";
975 Account const issuer{
"issuer"};
976 Account const subject{
"subject"};
982 env.
fund(
XRP(5000), subject, issuer);
996 params[jss::account] = subject.human();
997 auto const jv = env.rpc(
998 "json",
"account_tx",
to_string(params))[jss::result];
1000 BEAST_EXPECT(jv[jss::transactions].size() == 4);
1001 auto const& tx0(jv[jss::transactions][0u][jss::tx]);
1003 tx0[jss::TransactionType] == jss::CredentialAccept);
1004 auto const& tx1(jv[jss::transactions][1u][jss::tx]);
1006 tx1[jss::TransactionType] == jss::CredentialCreate);
1007 txHash0 = tx0[jss::hash].asString();
1008 txHash1 = tx1[jss::hash].asString();
1013 params[jss::account] = issuer.human();
1014 auto const jv = env.rpc(
1015 "json",
"account_tx",
to_string(params))[jss::result];
1017 BEAST_EXPECT(jv[jss::transactions].size() == 4);
1018 auto const& tx0(jv[jss::transactions][0u][jss::tx]);
1020 tx0[jss::TransactionType] == jss::CredentialAccept);
1021 auto const& tx1(jv[jss::transactions][1u][jss::tx]);
1023 tx1[jss::TransactionType] == jss::CredentialCreate);
1025 BEAST_EXPECT(txHash0 == tx0[jss::hash].asString());
1026 BEAST_EXPECT(txHash1 == tx1[jss::hash].asString());
1033 params[jss::account] = subject.human();
1035 "json",
"account_objects",
to_string(params))[jss::result];
1037 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
1038 auto const& object(jv[jss::account_objects][0u]);
1041 object[
"LedgerEntryType"].asString() == jss::Credential);
1042 objectIdx =
object[jss::index].asString();
1047 params[jss::account] = issuer.human();
1049 "json",
"account_objects",
to_string(params))[jss::result];
1051 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
1052 auto const& object(jv[jss::account_objects][0u]);
1055 object[
"LedgerEntryType"].asString() == jss::Credential);
1056 BEAST_EXPECT(objectIdx ==
object[jss::index].asString());
1064 using namespace test::jtx;
1066 bool const enabled = features[fixInvalidTxFlags];
1069 (enabled ?
"enabled" :
"disabled"));
1071 const char credType[] =
"abcde";
1072 Account const issuer{
"issuer"};
1073 Account const subject{
"subject"};
1076 using namespace jtx;
1077 Env env{*
this, features};
1079 env.fund(
XRP(5000), subject, issuer);
1101 using namespace test::jtx;
1115BEAST_DEFINE_TESTSUITE(Credentials, app,
ripple);
testcase_t testcase
Memberspace for declaring test cases.
An immutable linear range of bytes.
Immutable cryptographic account descriptor.
AccountID id() const
Returns the Account ID.
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)
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
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)
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 Keylet credentialKeylet(test::jtx::Account const &subject, test::jtx::Account const &issuer, std::string_view credType)
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
A pair of SHAMap key and LedgerEntryType.
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)