Permissioned Domains (XLS-80d) (#5161)

This commit is contained in:
Olek
2025-01-10 12:44:14 -05:00
committed by GitHub
parent 07f118caec
commit ccc0889803
35 changed files with 1962 additions and 109 deletions

View File

@@ -575,8 +575,8 @@ public:
Account const gw{"gateway"};
auto const USD = gw["USD"];
auto const features =
supported_amendments() | FeatureBitset{featureXChainBridge};
auto const features = supported_amendments() | featureXChainBridge |
featurePermissionedDomains;
Env env(*this, features);
// Make a lambda we can use to get "account_objects" easily.
@@ -627,6 +627,7 @@ public:
BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::ticket), 0));
BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::amm), 0));
BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::did), 0));
BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::permissioned_domain), 0));
// we expect invalid field type reported for the following types
BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::amendments)));
@@ -714,6 +715,47 @@ public:
BEAST_EXPECT(escrow[sfDestination.jsonName] == gw.human());
BEAST_EXPECT(escrow[sfAmount.jsonName].asUInt() == 100'000'000);
}
{
std::string const credentialType1 = "credential1";
Account issuer("issuer");
env.fund(XRP(5000), issuer);
// gw creates an PermissionedDomain.
env(pdomain::setTx(gw, {{issuer, credentialType1}}));
env.close();
// Find the PermissionedDomain.
Json::Value const resp = acctObjs(gw, jss::permissioned_domain);
BEAST_EXPECT(acctObjsIsSize(resp, 1));
auto const& permissionedDomain =
resp[jss::result][jss::account_objects][0u];
BEAST_EXPECT(
permissionedDomain.isMember(jss::Owner) &&
(permissionedDomain[jss::Owner] == gw.human()));
bool const check1 = BEAST_EXPECT(
permissionedDomain.isMember(jss::AcceptedCredentials) &&
permissionedDomain[jss::AcceptedCredentials].isArray() &&
(permissionedDomain[jss::AcceptedCredentials].size() == 1) &&
(permissionedDomain[jss::AcceptedCredentials][0u].isMember(
jss::Credential)));
if (check1)
{
auto const& credential =
permissionedDomain[jss::AcceptedCredentials][0u]
[jss::Credential];
BEAST_EXPECT(
credential.isMember(sfIssuer.jsonName) &&
(credential[sfIssuer.jsonName] == issuer.human()));
BEAST_EXPECT(
credential.isMember(sfCredentialType.jsonName) &&
(credential[sfCredentialType.jsonName] ==
strHex(credentialType1)));
}
}
{
// Create a bridge
test::jtx::XChainBridgeObjects x;
@@ -925,10 +967,13 @@ public:
BEAST_EXPECT(entry[sfAccount.jsonName] == alice.human());
BEAST_EXPECT(entry[sfSignerWeight.jsonName].asUInt() == 7);
}
// Create a Ticket for gw.
env(ticket::create(gw, 1));
env.close();
{
auto const seq = env.seq(gw);
// Create a Ticket for gw.
env(ticket::create(gw, 1));
env.close();
// Find the ticket.
Json::Value const resp = acctObjs(gw, jss::ticket);
BEAST_EXPECT(acctObjsIsSize(resp, 1));
@@ -936,8 +981,9 @@ public:
auto const& ticket = resp[jss::result][jss::account_objects][0u];
BEAST_EXPECT(ticket[sfAccount.jsonName] == gw.human());
BEAST_EXPECT(ticket[sfLedgerEntryType.jsonName] == jss::Ticket);
BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() == 14);
BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() == seq + 1);
}
{
// See how "deletion_blockers_only" handles gw's directory.
Json::Value params;
@@ -951,7 +997,8 @@ public:
jss::Check.c_str(),
jss::NFTokenPage.c_str(),
jss::RippleState.c_str(),
jss::PayChannel.c_str()};
jss::PayChannel.c_str(),
jss::PermissionedDomain.c_str()};
std::sort(v.begin(), v.end());
return v;
}();

View File

@@ -494,6 +494,18 @@ class LedgerRPC_test : public beast::unit_test::suite
"json", "ledger", "{ \"ledger_index\" : 1000000000000000 }");
checkErrorValue(ret, "invalidParams", "Invalid parameters.");
}
{
// ask for an zero index
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::index] =
"00000000000000000000000000000000000000000000000000000000000000"
"0000";
auto const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result];
checkErrorValue(jrr, "malformedRequest", "");
}
}
void
@@ -3086,6 +3098,122 @@ class LedgerRPC_test : public beast::unit_test::suite
}
}
void
testLedgerEntryPermissionedDomain()
{
testcase("ledger_entry PermissionedDomain");
using namespace test::jtx;
Env env(*this, supported_amendments() | featurePermissionedDomains);
Account const issuer{"issuer"};
Account const alice{"alice"};
Account const bob{"bob"};
env.fund(XRP(5000), issuer, alice, bob);
env.close();
auto const seq = env.seq(alice);
env(pdomain::setTx(alice, {{alice, "first credential"}}));
env.close();
auto const objects = pdomain::getObjects(alice, env);
if (!BEAST_EXPECT(objects.size() == 1))
return;
{
// Succeed
Json::Value params;
params[jss::ledger_index] = jss::validated;
params[jss::permissioned_domain][jss::account] = alice.human();
params[jss::permissioned_domain][jss::seq] = seq;
auto jv = env.rpc("json", "ledger_entry", to_string(params));
BEAST_EXPECT(
jv.isObject() && jv.isMember(jss::result) &&
!jv[jss::result].isMember(jss::error) &&
jv[jss::result].isMember(jss::node) &&
jv[jss::result][jss::node].isMember(
sfLedgerEntryType.jsonName) &&
jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
jss::PermissionedDomain);
std::string const pdIdx = jv[jss::result][jss::index].asString();
BEAST_EXPECT(
strHex(keylet::permissionedDomain(alice, seq).key) == pdIdx);
params.clear();
params[jss::ledger_index] = jss::validated;
params[jss::permissioned_domain] = pdIdx;
jv = env.rpc("json", "ledger_entry", to_string(params));
BEAST_EXPECT(
jv.isObject() && jv.isMember(jss::result) &&
!jv[jss::result].isMember(jss::error) &&
jv[jss::result].isMember(jss::node) &&
jv[jss::result][jss::node].isMember(
sfLedgerEntryType.jsonName) &&
jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
jss::PermissionedDomain);
}
{
// Fail, invalid permissioned domain index
Json::Value params;
params[jss::ledger_index] = jss::validated;
params[jss::permissioned_domain] =
"12F1F1F1F180D67377B2FAB292A31C922470326268D2B9B74CD1E582645B9A"
"DE";
auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
checkErrorValue(jrr[jss::result], "entryNotFound", "");
}
{
// Fail, invalid permissioned domain index
Json::Value params;
params[jss::ledger_index] = jss::validated;
params[jss::permissioned_domain] = "NotAHexString";
auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
checkErrorValue(jrr[jss::result], "malformedObjectId", "");
}
{
// Fail, permissioned domain is not an object
Json::Value params;
params[jss::ledger_index] = jss::validated;
params[jss::permissioned_domain] = 10;
auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
checkErrorValue(jrr[jss::result], "malformedObject", "");
}
{
// Fail, invalid account
Json::Value params;
params[jss::ledger_index] = jss::validated;
params[jss::permissioned_domain][jss::account] = 1;
params[jss::permissioned_domain][jss::seq] = seq;
auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
checkErrorValue(jrr[jss::result], "malformedAccount", "");
}
{
// Fail, no account
Json::Value params;
params[jss::ledger_index] = jss::validated;
params[jss::permissioned_domain][jss::account] = "";
params[jss::permissioned_domain][jss::seq] = seq;
auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
checkErrorValue(jrr[jss::result], "malformedAccount", "");
}
{
// Fail, invalid sequence
Json::Value params;
params[jss::ledger_index] = jss::validated;
params[jss::permissioned_domain][jss::account] = alice.human();
params[jss::permissioned_domain][jss::seq] = "12g";
auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
checkErrorValue(jrr[jss::result], "malformedSequence", "");
}
}
public:
void
run() override
@@ -3117,6 +3245,7 @@ public:
testOracleLedgerEntry();
testLedgerEntryMPT();
testLedgerEntryCLI();
testLedgerEntryPermissionedDomain();
forAllApiVersions(std::bind_front(
&LedgerRPC_test::testLedgerEntryInvalidParams, this));