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