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