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;
1076BEAST_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.
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)
A pair of SHAMap key and LedgerEntryType.
void testAcceptFailed(FeatureBitset features)
void testSuccessful(FeatureBitset features)
void testDeleteFailed(FeatureBitset features)
void testFeatureFailed(FeatureBitset features)
void testCredentialsDelete(FeatureBitset features)
void run() override
Runs the suite.
void testCreateFailed(FeatureBitset features)