rippled
Loading...
Searching...
No Matches
PermissionedDomains_test.cpp
1#include <test/jtx.h>
2
3#include <xrpld/app/tx/detail/PermissionedDomainSet.h>
4
5#include <xrpl/protocol/Feature.h>
6#include <xrpl/protocol/jss.h>
7
8#include <exception>
9#include <map>
10#include <optional>
11#include <string>
12#include <utility>
13#include <vector>
14
15namespace ripple {
16namespace test {
17
18using namespace jtx;
19
20static std::string
22{
23 try
24 {
25 env(jv, ter(temMALFORMED));
26 }
27 catch (std::exception const& ex)
28 {
29 return ex.what();
30 }
31 return {};
32}
33
35{
37 testable_amendments() - featurePermissionedDomains};
40 | featurePermissionedDomains | featureCredentials};
41
42 // Verify that each tx type can execute if the feature is enabled.
43 void
45 {
46 testcase("Enabled");
47 Account const alice("alice");
48 Env env(*this, withFeature_);
49 env.fund(XRP(1000), alice);
50 pdomain::Credentials credentials{{alice, "first credential"}};
51 env(pdomain::setTx(alice, credentials));
52 BEAST_EXPECT(env.ownerCount(alice) == 1);
53 auto objects = pdomain::getObjects(alice, env);
54 BEAST_EXPECT(objects.size() == 1);
55 // Test that account_objects is correct without passing it the type
56 BEAST_EXPECT(objects == pdomain::getObjects(alice, env, false));
57 auto const domain = objects.begin()->first;
58 env(pdomain::deleteTx(alice, domain));
59 }
60
61 // Verify that PD cannot be created or updated if credentials are disabled
62 void
64 {
65 auto amendments = testable_amendments();
66 amendments.set(featurePermissionedDomains);
67 amendments.reset(featureCredentials);
68 testcase("Credentials disabled");
69 Account const alice("alice");
70 Env env(*this, amendments);
71 env.fund(XRP(1000), alice);
72 pdomain::Credentials credentials{{alice, "first credential"}};
73 env(pdomain::setTx(alice, credentials), ter(temDISABLED));
74 }
75
76 // Verify that each tx does not execute if feature is disabled
77 void
79 {
80 testcase("Disabled");
81 Account const alice("alice");
82 Env env(*this, withoutFeature_);
83 env.fund(XRP(1000), alice);
84 pdomain::Credentials credentials{{alice, "first credential"}};
85 env(pdomain::setTx(alice, credentials), ter(temDISABLED));
86 env(pdomain::deleteTx(alice, uint256(75)), ter(temDISABLED));
87 }
88
89 // Verify that bad inputs fail for each of create new and update
90 // behaviors of PermissionedDomainSet
91 void
93 Account const& account,
94 Env& env,
96 {
97 Account const alice2("alice2");
98 Account const alice3("alice3");
99 Account const alice4("alice4");
100 Account const alice5("alice5");
101 Account const alice6("alice6");
102 Account const alice7("alice7");
103 Account const alice8("alice8");
104 Account const alice9("alice9");
105 Account const alice10("alice10");
106 Account const alice11("alice11");
107 Account const alice12("alice12");
108 auto const setFee(drops(env.current()->fees().increment));
109
110 // Test empty credentials.
113
114 // Test 11 credentials.
115 pdomain::Credentials const credentials11{
116 {alice2, "credential1"},
117 {alice3, "credential2"},
118 {alice4, "credential3"},
119 {alice5, "credential4"},
120 {alice6, "credential5"},
121 {alice7, "credential6"},
122 {alice8, "credential7"},
123 {alice9, "credential8"},
124 {alice10, "credential9"},
125 {alice11, "credential10"},
126 {alice12, "credential11"}};
127 BEAST_EXPECT(
128 credentials11.size() ==
130 env(pdomain::setTx(account, credentials11, domain),
132
133 // Test credentials including non-existent issuer.
134 Account const nobody("nobody");
135 pdomain::Credentials const credentialsNon{
136 {alice2, "credential1"},
137 {alice3, "credential2"},
138 {alice4, "credential3"},
139 {nobody, "credential4"},
140 {alice5, "credential5"},
141 {alice6, "credential6"},
142 {alice7, "credential7"}};
143 env(pdomain::setTx(account, credentialsNon, domain), ter(tecNO_ISSUER));
144
145 // Test bad fee
146 env(pdomain::setTx(account, credentials11, domain),
147 fee(1, true),
148 ter(temBAD_FEE));
149
150 pdomain::Credentials const credentials4{
151 {alice2, "credential1"},
152 {alice3, "credential2"},
153 {alice4, "credential3"},
154 {alice5, "credential4"},
155 };
156 auto txJsonMutable = pdomain::setTx(account, credentials4, domain);
157 auto const credentialOrig = txJsonMutable["AcceptedCredentials"][2u];
158
159 // Remove Issuer from a credential and apply.
160 txJsonMutable["AcceptedCredentials"][2u][jss::Credential].removeMember(
161 jss::Issuer);
162 BEAST_EXPECT(
163 exceptionExpected(env, txJsonMutable).starts_with("invalidParams"));
164
165 // Make an empty CredentialType.
166 txJsonMutable["AcceptedCredentials"][2u] = credentialOrig;
167 txJsonMutable["AcceptedCredentials"][2u][jss::Credential]
168 ["CredentialType"] = "";
169 env(txJsonMutable, ter(temMALFORMED));
170
171 // Make too long CredentialType.
172 constexpr std::string_view longCredentialType =
173 "Cred0123456789012345678901234567890123456789012345678901234567890";
174 static_assert(longCredentialType.size() == maxCredentialTypeLength + 1);
175 txJsonMutable["AcceptedCredentials"][2u] = credentialOrig;
176 txJsonMutable["AcceptedCredentials"][2u][jss::Credential]
177 ["CredentialType"] = std::string(longCredentialType);
178 BEAST_EXPECT(
179 exceptionExpected(env, txJsonMutable).starts_with("invalidParams"));
180
181 // Remove Credentialtype from a credential and apply.
182 txJsonMutable["AcceptedCredentials"][2u][jss::Credential].removeMember(
183 "CredentialType");
184 BEAST_EXPECT(
185 exceptionExpected(env, txJsonMutable).starts_with("invalidParams"));
186
187 // Remove both
188 txJsonMutable["AcceptedCredentials"][2u][jss::Credential].removeMember(
189 jss::Issuer);
190 BEAST_EXPECT(
191 exceptionExpected(env, txJsonMutable).starts_with("invalidParams"));
192
193 // Make 2 identical credentials. Duplicates are not supported by
194 // permissioned domains, so transactions should return errors
195 {
196 pdomain::Credentials const credentialsDup{
197 {alice7, "credential6"},
198 {alice2, "credential1"},
199 {alice3, "credential2"},
200 {alice2, "credential1"},
201 {alice5, "credential4"},
202 };
203
205 for (auto const& c : credentialsDup)
206 human2Acc.emplace(c.issuer.human(), c.issuer);
207
208 auto const sorted = pdomain::sortCredentials(credentialsDup);
209 BEAST_EXPECT(sorted.size() == 4);
210 env(pdomain::setTx(account, credentialsDup, domain),
212
213 env.close();
214 env(pdomain::setTx(account, sorted, domain));
215
216 uint256 d;
217 if (domain)
218 d = *domain;
219 else
220 d = pdomain::getNewDomain(env.meta());
221 env.close();
222 auto objects = pdomain::getObjects(account, env);
223 auto const fromObject =
224 pdomain::credentialsFromJson(objects[d], human2Acc);
225 auto const sortedCreds = pdomain::sortCredentials(credentialsDup);
226 BEAST_EXPECT(fromObject == sortedCreds);
227 }
228
229 // Have equal issuers but different credentials and make sure they
230 // sort correctly.
231 {
232 pdomain::Credentials const credentialsSame{
233 {alice2, "credential3"},
234 {alice3, "credential2"},
235 {alice2, "credential9"},
236 {alice5, "credential4"},
237 {alice2, "credential6"},
238 };
240 for (auto const& c : credentialsSame)
241 human2Acc.emplace(c.issuer.human(), c.issuer);
242
243 BEAST_EXPECT(
244 credentialsSame != pdomain::sortCredentials(credentialsSame));
245 env(pdomain::setTx(account, credentialsSame, domain));
246
247 uint256 d;
248 if (domain)
249 d = *domain;
250 else
251 d = pdomain::getNewDomain(env.meta());
252 env.close();
253 auto objects = pdomain::getObjects(account, env);
254 auto const fromObject =
255 pdomain::credentialsFromJson(objects[d], human2Acc);
256 auto const sortedCreds = pdomain::sortCredentials(credentialsSame);
257 BEAST_EXPECT(fromObject == sortedCreds);
258 }
259 }
260
261 // Test PermissionedDomainSet
262 void
264 {
265 testcase("Set");
266 Env env(*this, withFeature_);
268
269 int const accNum = 12;
270 Account const alice[accNum] = {
271 "alice",
272 "alice2",
273 "alice3",
274 "alice4",
275 "alice5",
276 "alice6",
277 "alice7",
278 "alice8",
279 "alice9",
280 "alice10",
281 "alice11",
282 "alice12"};
284 for (auto const& c : alice)
285 human2Acc.emplace(c.human(), c);
286
287 for (int i = 0; i < accNum; ++i)
288 env.fund(XRP(1000), alice[i]);
289
290 // Create new from existing account with a single credential.
291 pdomain::Credentials const credentials1{{alice[2], "credential1"}};
292 {
293 env(pdomain::setTx(alice[0], credentials1));
294 BEAST_EXPECT(env.ownerCount(alice[0]) == 1);
295 auto tx = env.tx()->getJson(JsonOptions::none);
296 BEAST_EXPECT(tx[jss::TransactionType] == "PermissionedDomainSet");
297 BEAST_EXPECT(tx["Account"] == alice[0].human());
298 auto objects = pdomain::getObjects(alice[0], env);
299 auto domain = objects.begin()->first;
300 BEAST_EXPECT(domain.isNonZero());
301 auto object = objects.begin()->second;
302 BEAST_EXPECT(object["LedgerEntryType"] == "PermissionedDomain");
303 BEAST_EXPECT(object["Owner"] == alice[0].human());
304 BEAST_EXPECT(object["Sequence"] == tx["Sequence"]);
305 BEAST_EXPECT(
306 pdomain::credentialsFromJson(object, human2Acc) ==
307 credentials1);
308 }
309
310 // Make longest possible CredentialType.
311 {
312 constexpr std::string_view longCredentialType =
313 "Cred0123456789012345678901234567890123456789012345678901234567"
314 "89";
315 static_assert(longCredentialType.size() == maxCredentialTypeLength);
316 pdomain::Credentials const longCredentials{
317 {alice[1], std::string(longCredentialType)}};
318
319 env(pdomain::setTx(alice[0], longCredentials));
320
321 // One account can create multiple domains
322 BEAST_EXPECT(env.ownerCount(alice[0]) == 2);
323
324 auto tx = env.tx()->getJson(JsonOptions::none);
325 BEAST_EXPECT(tx[jss::TransactionType] == "PermissionedDomainSet");
326 BEAST_EXPECT(tx["Account"] == alice[0].human());
327
328 bool findSeq = false;
329 for (auto const& [domain, object] :
330 pdomain::getObjects(alice[0], env))
331 {
332 findSeq = object["Sequence"] == tx["Sequence"];
333 if (findSeq)
334 {
335 BEAST_EXPECT(domain.isNonZero());
336 BEAST_EXPECT(
337 object["LedgerEntryType"] == "PermissionedDomain");
338 BEAST_EXPECT(object["Owner"] == alice[0].human());
339 BEAST_EXPECT(
340 pdomain::credentialsFromJson(object, human2Acc) ==
341 longCredentials);
342 break;
343 }
344 }
345 BEAST_EXPECT(findSeq);
346 }
347
348 // Create new from existing account with 10 credentials.
349 // Last credential describe domain owner itself
350 pdomain::Credentials const credentials10{
351 {alice[2], "credential1"},
352 {alice[3], "credential2"},
353 {alice[4], "credential3"},
354 {alice[5], "credential4"},
355 {alice[6], "credential5"},
356 {alice[7], "credential6"},
357 {alice[8], "credential7"},
358 {alice[9], "credential8"},
359 {alice[10], "credential9"},
360 {alice[0], "credential10"},
361 };
362 uint256 domain2;
363 {
364 BEAST_EXPECT(
365 credentials10.size() ==
367 BEAST_EXPECT(
368 credentials10 != pdomain::sortCredentials(credentials10));
369 env(pdomain::setTx(alice[0], credentials10));
370 auto tx = env.tx()->getJson(JsonOptions::none);
371 domain2 = pdomain::getNewDomain(env.meta());
372 auto objects = pdomain::getObjects(alice[0], env);
373 auto object = objects[domain2];
374 BEAST_EXPECT(
375 pdomain::credentialsFromJson(object, human2Acc) ==
376 pdomain::sortCredentials(credentials10));
377 }
378
379 // Update with 1 credential.
380 env(pdomain::setTx(alice[0], credentials1, domain2));
381 BEAST_EXPECT(
383 pdomain::getObjects(alice[0], env)[domain2], human2Acc) ==
384 credentials1);
385
386 // Update with 10 credentials.
387 env(pdomain::setTx(alice[0], credentials10, domain2));
388 env.close();
389 BEAST_EXPECT(
391 pdomain::getObjects(alice[0], env)[domain2], human2Acc) ==
392 pdomain::sortCredentials(credentials10));
393
394 // Update from the wrong owner.
395 env(pdomain::setTx(alice[2], credentials1, domain2),
397
398 // Update a uint256(0) domain
399 env(pdomain::setTx(alice[0], credentials1, uint256(0)),
401
402 // Update non-existent domain
403 env(pdomain::setTx(alice[0], credentials1, uint256(75)),
405
406 // Wrong flag
407 env(pdomain::setTx(alice[0], credentials1),
410
411 // Test bad data when creating a domain.
412 testBadData(alice[0], env);
413 // Test bad data when updating a domain.
414 testBadData(alice[0], env, domain2);
415
416 // Try to delete the account with domains.
417 auto const acctDelFee(drops(env.current()->fees().increment));
418 constexpr std::size_t deleteDelta = 255;
419 {
420 // Close enough ledgers to make it potentially deletable if empty.
421 std::size_t ownerSeq = env.seq(alice[0]);
422 while (deleteDelta + ownerSeq > env.current()->seq())
423 env.close();
424 env(acctdelete(alice[0], alice[2]),
425 fee(acctDelFee),
427 }
428
429 {
430 // Delete the domains and then the owner account.
431 for (auto const& objs : pdomain::getObjects(alice[0], env))
432 env(pdomain::deleteTx(alice[0], objs.first));
433 env.close();
434 std::size_t ownerSeq = env.seq(alice[0]);
435 while (deleteDelta + ownerSeq > env.current()->seq())
436 env.close();
437 env(acctdelete(alice[0], alice[2]), fee(acctDelFee));
438 }
439 }
440
441 // Test PermissionedDomainDelete
442 void
444 {
445 testcase("Delete");
446 Env env(*this, withFeature_);
447 Account const alice("alice");
448
449 env.fund(XRP(1000), alice);
450 auto const setFee(drops(env.current()->fees().increment));
451
452 pdomain::Credentials credentials{{alice, "first credential"}};
453 env(pdomain::setTx(alice, credentials));
454 env.close();
455
456 auto objects = pdomain::getObjects(alice, env);
457 BEAST_EXPECT(objects.size() == 1);
458 auto const domain = objects.begin()->first;
459
460 // Delete a domain that doesn't belong to the account.
461 Account const bob("bob");
462 env.fund(XRP(1000), bob);
464
465 // Delete a non-existent domain.
466 env(pdomain::deleteTx(alice, uint256(75)), ter(tecNO_ENTRY));
467
468 // Test bad fee
469 env(pdomain::deleteTx(alice, uint256(75)),
471 fee(1, true));
472
473 // Wrong flag
474 env(pdomain::deleteTx(alice, domain),
477
478 // Delete a zero domain.
479 env(pdomain::deleteTx(alice, uint256(0)), ter(temMALFORMED));
480
481 // Make sure owner count reflects the existing domain.
482 BEAST_EXPECT(env.ownerCount(alice) == 1);
483 auto const objID = pdomain::getObjects(alice, env).begin()->first;
484 BEAST_EXPECT(pdomain::objectExists(objID, env));
485
486 // Delete domain that belongs to user.
487 env(pdomain::deleteTx(alice, domain));
488 auto const tx = env.tx()->getJson(JsonOptions::none);
489 BEAST_EXPECT(tx[jss::TransactionType] == "PermissionedDomainDelete");
490
491 // Make sure the owner count goes back to 0.
492 BEAST_EXPECT(env.ownerCount(alice) == 0);
493
494 // The object needs to be gone.
495 BEAST_EXPECT(pdomain::getObjects(alice, env).empty());
496 BEAST_EXPECT(!pdomain::objectExists(objID, env));
497 }
498
499 void
501 {
502 // Verify that the reserve behaves as expected for creating.
503 testcase("Account Reserve");
504
505 using namespace test::jtx;
506
507 Env env(*this, withFeature_);
508 Account const alice("alice");
509
510 // Fund alice enough to exist, but not enough to meet
511 // the reserve.
512 auto const acctReserve = env.current()->fees().reserve;
513 auto const incReserve = env.current()->fees().increment;
514 env.fund(acctReserve, alice);
515 env.close();
516 BEAST_EXPECT(env.balance(alice) == acctReserve);
517 BEAST_EXPECT(env.ownerCount(alice) == 0);
518
519 // alice does not have enough XRP to cover the reserve.
520 pdomain::Credentials credentials{{alice, "first credential"}};
521 env(pdomain::setTx(alice, credentials), ter(tecINSUFFICIENT_RESERVE));
522 BEAST_EXPECT(env.ownerCount(alice) == 0);
523 BEAST_EXPECT(pdomain::getObjects(alice, env).size() == 0);
524 env.close();
525
526 auto const baseFee = env.current()->fees().base.drops();
527
528 // Pay alice almost enough to make the reserve.
529 env(pay(env.master, alice, incReserve + drops(2 * baseFee) - drops(1)));
530 BEAST_EXPECT(
531 env.balance(alice) ==
532 acctReserve + incReserve + drops(baseFee) - drops(1));
533 env.close();
534
535 // alice still does not have enough XRP for the reserve.
536 env(pdomain::setTx(alice, credentials), ter(tecINSUFFICIENT_RESERVE));
537 env.close();
538 BEAST_EXPECT(env.ownerCount(alice) == 0);
539
540 // Pay alice enough to make the reserve.
541 env(pay(env.master, alice, drops(baseFee) + drops(1)));
542 env.close();
543
544 // Now alice can create a PermissionedDomain.
545 env(pdomain::setTx(alice, credentials));
546 env.close();
547 BEAST_EXPECT(env.ownerCount(alice) == 1);
548 }
549
550public:
551 void
552 run() override
553 {
554 testEnabled();
556 testDisabled();
557 testSet();
558 testDelete();
560 }
561};
562
563BEAST_DEFINE_TESTSUITE(PermissionedDomains, app, ripple);
564
565} // namespace test
566} // namespace ripple
Represents a JSON value.
Definition json_value.h:130
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
void testBadData(Account const &account, Env &env, std::optional< uint256 > domain=std::nullopt)
Immutable cryptographic account descriptor.
Definition Account.h:20
A transaction testing environment.
Definition Env.h:102
std::uint32_t ownerCount(Account const &account) const
Return the number of objects owned by an account.
Definition Env.cpp:241
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:250
std::shared_ptr< STTx const > tx() const
Return the tx data for the last JTx.
Definition Env.cpp:507
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:312
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:103
Account const & master
Definition Env.h:106
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:271
std::shared_ptr< STObject const > meta()
Return metadata for the last JTx.
Definition Env.cpp:485
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:165
void set_parse_failure_expected(bool b)
Definition Env.h:397
Set the domain on a JTx.
Definition domain.h:11
Set the fee on a JTx.
Definition fee.h:18
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:16
Set the flags on a JTx.
Definition txflags.h:12
T emplace(T... args)
T is_same_v
Credentials sortCredentials(Credentials const &input)
Credentials credentialsFromJson(Json::Value const &object, std::unordered_map< std::string, Account > const &human2Acc)
bool objectExists(uint256 const &objID, Env &env)
Json::Value deleteTx(AccountID const &account, uint256 const &domain)
Json::Value setTx(AccountID const &account, Credentials const &credentials, std::optional< uint256 > domain)
std::map< uint256, Json::Value > getObjects(Account const &account, Env &env, bool withType)
uint256 getNewDomain(std::shared_ptr< STObject const > const &meta)
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:55
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
static std::string exceptionExpected(Env &env, Json::Value const &jv)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
base_uint< 256 > uint256
Definition base_uint.h:539
std::size_t constexpr maxPermissionedDomainCredentialsArraySize
The maximum number of credentials can be passed in array for permissioned domain.
Definition Protocol.h:94
constexpr std::uint32_t tfClawTwoAssets
Definition TxFlags.h:243
std::size_t constexpr maxCredentialTypeLength
The maximum length of a CredentialType inside a Credential.
Definition Protocol.h:87
@ tecNO_ENTRY
Definition TER.h:288
@ tecNO_ISSUER
Definition TER.h:281
@ tecNO_PERMISSION
Definition TER.h:287
@ tecHAS_OBLIGATIONS
Definition TER.h:299
@ tecINSUFFICIENT_RESERVE
Definition TER.h:289
@ temBAD_FEE
Definition TER.h:73
@ temMALFORMED
Definition TER.h:68
@ temINVALID_FLAG
Definition TER.h:92
@ temARRAY_EMPTY
Definition TER.h:121
@ temARRAY_TOO_LARGE
Definition TER.h:122
@ temDISABLED
Definition TER.h:95
T size(T... args)
T what(T... args)