rippled
Loading...
Searching...
No Matches
NFTokenDir_test.cpp
1#include <test/jtx.h>
2
3#include <xrpld/app/tx/detail/NFTokenUtils.h>
4
5#include <xrpl/protocol/Feature.h>
6#include <xrpl/protocol/jss.h>
7#include <xrpl/protocol/nftPageMask.h>
8
9#include <initializer_list>
10
11namespace ripple {
12
14{
15 // printNFTPages is a helper function that may be used for debugging.
16 //
17 // It uses the ledger RPC command to show the NFT pages in the ledger.
18 // This parameter controls how noisy the output is.
19 enum Volume : bool {
20 quiet = false,
21 noisy = true,
22 };
23
24 void
26 {
27 Json::Value jvParams;
28 jvParams[jss::ledger_index] = "current";
29 jvParams[jss::binary] = false;
30 {
31 Json::Value jrr =
32 env.rpc("json", "ledger_data", to_string(jvParams));
33
34 // Iterate the state and print all NFTokenPages.
35 if (!jrr.isMember(jss::result) ||
36 !jrr[jss::result].isMember(jss::state))
37 {
38 std::cout << "No ledger state found!" << std::endl;
39 return;
40 }
41 Json::Value& state = jrr[jss::result][jss::state];
42 if (!state.isArray())
43 {
44 std::cout << "Ledger state is not array!" << std::endl;
45 return;
46 }
47 for (Json::UInt i = 0; i < state.size(); ++i)
48 {
49 if (state[i].isMember(sfNFTokens.jsonName) &&
50 state[i][sfNFTokens.jsonName].isArray())
51 {
52 std::uint32_t tokenCount =
53 state[i][sfNFTokens.jsonName].size();
54 std::cout << tokenCount << " NFtokens in page "
55 << state[i][jss::index].asString() << std::endl;
56
57 if (vol == noisy)
58 {
59 std::cout << state[i].toStyledString() << std::endl;
60 }
61 else
62 {
63 if (tokenCount > 0)
64 std::cout << "first: "
65 << state[i][sfNFTokens.jsonName][0u]
67 << std::endl;
68 if (tokenCount > 1)
70 << "last: "
71 << state[i][sfNFTokens.jsonName][tokenCount - 1]
73 << std::endl;
74 }
75 }
76 }
77 }
78 }
79
80 void
82 {
83 // It should be possible to store many consecutive NFTs.
84 testcase("Sequential NFTs");
85
86 using namespace test::jtx;
87 Env env{*this, features};
88
89 // A single minter tends not to mint numerically sequential NFTokens
90 // because the taxon cipher mixes things up. We can override the
91 // cipher, however, and mint many sequential NFTokens with no gaps
92 // between them.
93 //
94 // Here we'll simply mint 100 sequential NFTs. Then we'll create
95 // offers for them to verify that the ledger can find them.
96
97 Account const issuer{"issuer"};
98 Account const buyer{"buyer"};
99 env.fund(XRP(10000), buyer, issuer);
100 env.close();
101
102 // Mint 100 sequential NFTs. Tweak the taxon so zero is always stored.
103 // That's what makes them sequential.
104 constexpr std::size_t nftCount = 100;
106 nftIDs.reserve(nftCount);
107 for (int i = 0; i < nftCount; ++i)
108 {
109 std::uint32_t taxon =
110 toUInt32(nft::cipheredTaxon(i, nft::toTaxon(0)));
111 nftIDs.emplace_back(
112 token::getNextID(env, issuer, taxon, tfTransferable));
113 env(token::mint(issuer, taxon), txflags(tfTransferable));
114 env.close();
115 }
116
117 // Create an offer for each of the NFTs. This verifies that the ledger
118 // can find all of the minted NFTs.
120 for (uint256 const& nftID : nftIDs)
121 {
122 offers.emplace_back(keylet::nftoffer(issuer, env.seq(issuer)).key);
123 env(token::createOffer(issuer, nftID, XRP(0)),
124 txflags((tfSellNFToken)));
125 env.close();
126 }
127
128 // Buyer accepts all of the offers in reverse order.
129 std::reverse(offers.begin(), offers.end());
130 for (uint256 const& offer : offers)
131 {
132 env(token::acceptSellOffer(buyer, offer));
133 env.close();
134 }
135 }
136
137 void
139 {
140 // All NFT IDs with the same low 96 bits must stay on the same NFT page.
141 testcase("Lopsided splits");
142
143 using namespace test::jtx;
144
145 // When a single NFT page exceeds 32 entries, the code is inclined
146 // to split that page into two equal pieces. That's fine, but
147 // the code also needs to keep NFTs with identical low 96-bits on
148 // the same page.
149 //
150 // Here we synthesize cases where there are several NFTs with
151 // identical 96-low-bits in the middle of a page. When that page
152 // is split because it overflows, we need to see that the NFTs
153 // with identical 96-low-bits are all kept on the same page.
154
155 // Lambda that exercises the lopsided splits.
156 auto exerciseLopsided =
157 [this,
159 Env env{*this, features};
160
161 // Eventually all of the NFTokens will be owned by buyer.
162 Account const buyer{"buyer"};
163 env.fund(XRP(10000), buyer);
164 env.close();
165
166 // Create accounts for all of the seeds and fund those accounts.
167 std::vector<Account> accounts;
168 accounts.reserve(seeds.size());
169 for (std::string_view seed : seeds)
170 {
171 Account const& account = accounts.emplace_back(
172 Account::base58Seed, std::string(seed));
173 env.fund(XRP(10000), account);
174
175 // Do not close the ledger inside the loop. If accounts are
176 // initialized at different ledgers, they will have
177 // different account sequences. That would cause the
178 // accounts to have different NFTokenID sequence numbers.
179 }
180 env.close();
181
182 // All of the accounts create one NFT and and offer that NFT to
183 // buyer.
186 offers.reserve(accounts.size());
187 for (Account const& account : accounts)
188 {
189 // Mint the NFT.
190 uint256 const& nftID = nftIDs.emplace_back(
191 token::getNextID(env, account, 0, tfTransferable));
192 env(token::mint(account, 0), txflags(tfTransferable));
193 env.close();
194
195 // Create an offer to give the NFT to buyer for free.
196 offers.emplace_back(
197 keylet::nftoffer(account, env.seq(account)).key);
198 env(token::createOffer(account, nftID, XRP(0)),
199 token::destination(buyer),
200 txflags((tfSellNFToken)));
201 }
202 env.close();
203
204 // buyer accepts all of the offers.
205 for (uint256 const& offer : offers)
206 {
207 env(token::acceptSellOffer(buyer, offer));
208 env.close();
209 }
210
211 // This can be a good time to look at the NFT pages.
212 // printNFTPages(env, noisy);
213
214 // Verify that all NFTs are owned by buyer and findable in the
215 // ledger by having buyer create sell offers for all of their
216 // NFTs. Attempting to sell an offer that the ledger can't find
217 // generates a non-tesSUCCESS error code.
218 for (uint256 const& nftID : nftIDs)
219 {
220 uint256 const offerID =
221 keylet::nftoffer(buyer, env.seq(buyer)).key;
222 env(token::createOffer(buyer, nftID, XRP(100)),
223 txflags(tfSellNFToken));
224 env.close();
225
226 env(token::cancelOffer(buyer, {offerID}));
227 }
228
229 // Verify that all the NFTs are owned by buyer.
230 Json::Value buyerNFTs = [&env, &buyer]() {
231 Json::Value params;
232 params[jss::account] = buyer.human();
233 params[jss::type] = "state";
234 return env.rpc("json", "account_nfts", to_string(params));
235 }();
236
237 BEAST_EXPECT(
238 buyerNFTs[jss::result][jss::account_nfts].size() ==
239 nftIDs.size());
240 for (Json::Value const& ownedNFT :
241 buyerNFTs[jss::result][jss::account_nfts])
242 {
243 uint256 ownedID;
244 BEAST_EXPECT(ownedID.parseHex(
245 ownedNFT[sfNFTokenID.jsonName].asString()));
246 auto const foundIter =
247 std::find(nftIDs.begin(), nftIDs.end(), ownedID);
248
249 // Assuming we find the NFT, erase it so we know it's been
250 // found and can't be found again.
251 if (BEAST_EXPECT(foundIter != nftIDs.end()))
252 nftIDs.erase(foundIter);
253 }
254
255 // All NFTs should now be accounted for, so nftIDs should be
256 // empty.
257 BEAST_EXPECT(nftIDs.empty());
258 };
259
260 // These seeds cause a lopsided split where the new NFT is added
261 // to the upper page.
263 splitAndAddToHi{
264 "sp6JS7f14BuwFY8Mw5p3b8jjQBBTK", // 0. 0x1d2932ea
265 "sp6JS7f14BuwFY8Mw6F7X3EiGKazu", // 1. 0x1d2932ea
266 "sp6JS7f14BuwFY8Mw6FxjntJJfKXq", // 2. 0x1d2932ea
267 "sp6JS7f14BuwFY8Mw6eSF1ydEozJg", // 3. 0x1d2932ea
268 "sp6JS7f14BuwFY8Mw6koPB91um2ej", // 4. 0x1d2932ea
269 "sp6JS7f14BuwFY8Mw6m6D64iwquSe", // 5. 0x1d2932ea
270
271 "sp6JS7f14BuwFY8Mw5rC43sN4adC2", // 6. 0x208dbc24
272 "sp6JS7f14BuwFY8Mw65L9DDQqgebz", // 7. 0x208dbc24
273 "sp6JS7f14BuwFY8Mw65nKvU8pPQNn", // 8. 0x208dbc24
274 "sp6JS7f14BuwFY8Mw6bxZLyTrdipw", // 9. 0x208dbc24
275 "sp6JS7f14BuwFY8Mw6d5abucntSoX", // 10. 0x208dbc24
276 "sp6JS7f14BuwFY8Mw6qXK5awrRRP8", // 11. 0x208dbc24
277
278 // These eight need to be kept together by the implementation.
279 "sp6JS7f14BuwFY8Mw66EBtMxoMcCa", // 12. 0x309b67ed
280 "sp6JS7f14BuwFY8Mw66dGfE9jVfGv", // 13. 0x309b67ed
281 "sp6JS7f14BuwFY8Mw6APdZa7PH566", // 14. 0x309b67ed
282 "sp6JS7f14BuwFY8Mw6C3QX5CZyET5", // 15. 0x309b67ed
283 "sp6JS7f14BuwFY8Mw6CSysFf8GvaR", // 16. 0x309b67ed
284 "sp6JS7f14BuwFY8Mw6c7QSDmoAeRV", // 17. 0x309b67ed
285 "sp6JS7f14BuwFY8Mw6mvonveaZhW7", // 18. 0x309b67ed
286 "sp6JS7f14BuwFY8Mw6vtHHG7dYcXi", // 19. 0x309b67ed
287
288 "sp6JS7f14BuwFY8Mw66yppUNxESaw", // 20. 0x40d4b96f
289 "sp6JS7f14BuwFY8Mw6ATYQvobXiDT", // 21. 0x40d4b96f
290 "sp6JS7f14BuwFY8Mw6bis8D1Wa9Uy", // 22. 0x40d4b96f
291 "sp6JS7f14BuwFY8Mw6cTiGCWA8Wfa", // 23. 0x40d4b96f
292 "sp6JS7f14BuwFY8Mw6eAy2fpXmyYf", // 24. 0x40d4b96f
293 "sp6JS7f14BuwFY8Mw6icn58TRs8YG", // 25. 0x40d4b96f
294
295 "sp6JS7f14BuwFY8Mw68tj2eQEWoJt", // 26. 0x503b6ba9
296 "sp6JS7f14BuwFY8Mw6AjnAinNnMHT", // 27. 0x503b6ba9
297 "sp6JS7f14BuwFY8Mw6CKDUwB4LrhL", // 28. 0x503b6ba9
298 "sp6JS7f14BuwFY8Mw6d2yPszEFA6J", // 29. 0x503b6ba9
299 "sp6JS7f14BuwFY8Mw6jcBQBH3PfnB", // 30. 0x503b6ba9
300 "sp6JS7f14BuwFY8Mw6qxx19KSnN1w", // 31. 0x503b6ba9
301
302 // Adding this NFT splits the page. It is added to the upper
303 // page.
304 "sp6JS7f14BuwFY8Mw6ut1hFrqWoY5", // 32. 0x503b6ba9
305 };
306
307 // These seeds cause a lopsided split where the new NFT is added
308 // to the lower page.
310 splitAndAddToLo{
311 "sp6JS7f14BuwFY8Mw5p3b8jjQBBTK", // 0. 0x1d2932ea
312 "sp6JS7f14BuwFY8Mw6F7X3EiGKazu", // 1. 0x1d2932ea
313 "sp6JS7f14BuwFY8Mw6FxjntJJfKXq", // 2. 0x1d2932ea
314 "sp6JS7f14BuwFY8Mw6eSF1ydEozJg", // 3. 0x1d2932ea
315 "sp6JS7f14BuwFY8Mw6koPB91um2ej", // 4. 0x1d2932ea
316 "sp6JS7f14BuwFY8Mw6m6D64iwquSe", // 5. 0x1d2932ea
317
318 "sp6JS7f14BuwFY8Mw5rC43sN4adC2", // 6. 0x208dbc24
319 "sp6JS7f14BuwFY8Mw65L9DDQqgebz", // 7. 0x208dbc24
320 "sp6JS7f14BuwFY8Mw65nKvU8pPQNn", // 8. 0x208dbc24
321 "sp6JS7f14BuwFY8Mw6bxZLyTrdipw", // 9. 0x208dbc24
322 "sp6JS7f14BuwFY8Mw6d5abucntSoX", // 10. 0x208dbc24
323 "sp6JS7f14BuwFY8Mw6qXK5awrRRP8", // 11. 0x208dbc24
324
325 // These eight need to be kept together by the implementation.
326 "sp6JS7f14BuwFY8Mw66EBtMxoMcCa", // 12. 0x309b67ed
327 "sp6JS7f14BuwFY8Mw66dGfE9jVfGv", // 13. 0x309b67ed
328 "sp6JS7f14BuwFY8Mw6APdZa7PH566", // 14. 0x309b67ed
329 "sp6JS7f14BuwFY8Mw6C3QX5CZyET5", // 15. 0x309b67ed
330 "sp6JS7f14BuwFY8Mw6CSysFf8GvaR", // 16. 0x309b67ed
331 "sp6JS7f14BuwFY8Mw6c7QSDmoAeRV", // 17. 0x309b67ed
332 "sp6JS7f14BuwFY8Mw6mvonveaZhW7", // 18. 0x309b67ed
333 "sp6JS7f14BuwFY8Mw6vtHHG7dYcXi", // 19. 0x309b67ed
334
335 "sp6JS7f14BuwFY8Mw66yppUNxESaw", // 20. 0x40d4b96f
336 "sp6JS7f14BuwFY8Mw6ATYQvobXiDT", // 21. 0x40d4b96f
337 "sp6JS7f14BuwFY8Mw6bis8D1Wa9Uy", // 22. 0x40d4b96f
338 "sp6JS7f14BuwFY8Mw6cTiGCWA8Wfa", // 23. 0x40d4b96f
339 "sp6JS7f14BuwFY8Mw6eAy2fpXmyYf", // 24. 0x40d4b96f
340 "sp6JS7f14BuwFY8Mw6icn58TRs8YG", // 25. 0x40d4b96f
341
342 "sp6JS7f14BuwFY8Mw68tj2eQEWoJt", // 26. 0x503b6ba9
343 "sp6JS7f14BuwFY8Mw6AjnAinNnMHT", // 27. 0x503b6ba9
344 "sp6JS7f14BuwFY8Mw6CKDUwB4LrhL", // 28. 0x503b6ba9
345 "sp6JS7f14BuwFY8Mw6d2yPszEFA6J", // 29. 0x503b6ba9
346 "sp6JS7f14BuwFY8Mw6jcBQBH3PfnB", // 30. 0x503b6ba9
347 "sp6JS7f14BuwFY8Mw6qxx19KSnN1w", // 31. 0x503b6ba9
348
349 // Adding this NFT splits the page. It is added to the lower
350 // page.
351 "sp6JS7f14BuwFY8Mw6xCigaMwC6Dp", // 32. 0x309b67ed
352 };
353
354 // Run the test cases.
355 exerciseLopsided(splitAndAddToHi);
356 exerciseLopsided(splitAndAddToLo);
357 }
358
359 void
361 {
362 testcase("NFTokenDir");
363
364 using namespace test::jtx;
365
366 // When a single NFT page exceeds 32 entries, the code is inclined
367 // to split that page into two equal pieces. The new page is lower
368 // than the original. There was an off-by-one in the selection of
369 // the index for the new page. This test recreates the problem.
370
371 // Lambda that exercises the split.
372 auto exercise =
373 [this,
375 Env env{
376 *this,
377 envconfig(),
378 features,
379 nullptr,
381
382 // Eventually all of the NFTokens will be owned by buyer.
383 Account const buyer{"buyer"};
384 env.fund(XRP(10000), buyer);
385 env.close();
386
387 // Create accounts for all of the seeds and fund those accounts.
388 std::vector<Account> accounts;
389 accounts.reserve(seeds.size());
390 for (std::string_view seed : seeds)
391 {
392 Account const& account = accounts.emplace_back(
393 Account::base58Seed, std::string(seed));
394 env.fund(XRP(10000), account);
395
396 // Do not close the ledger inside the loop. If accounts are
397 // initialized at different ledgers, they will have
398 // different account sequences. That would cause the
399 // accounts to have different NFTokenID sequence numbers.
400 }
401 env.close();
402
403 // All of the accounts create one NFT and and offer that NFT to
404 // buyer.
407 offers.reserve(accounts.size());
408 for (Account const& account : accounts)
409 {
410 // Mint the NFT.
411 uint256 const& nftID = nftIDs.emplace_back(
412 token::getNextID(env, account, 0, tfTransferable));
413 env(token::mint(account, 0), txflags(tfTransferable));
414 env.close();
415
416 // Create an offer to give the NFT to buyer for free.
417 offers.emplace_back(
418 keylet::nftoffer(account, env.seq(account)).key);
419 env(token::createOffer(account, nftID, XRP(0)),
420 token::destination(buyer),
421 txflags((tfSellNFToken)));
422 }
423 env.close();
424
425 // buyer accepts all of the but the last. The last offer
426 // causes the page to split.
427 for (std::size_t i = 0; i < offers.size() - 1; ++i)
428 {
429 env(token::acceptSellOffer(buyer, offers[i]));
430 env.close();
431 }
432
433 env(token::acceptSellOffer(buyer, offers.back()));
434 env.close();
435
436 // This can be a good time to look at the NFT pages.
437 // printNFTPages(env, noisy);
438
439 // Verify that all NFTs are owned by buyer and findable in the
440 // ledger by having buyer create sell offers for all of their
441 // NFTs. Attempting to sell an offer that the ledger can't find
442 // generates a non-tesSUCCESS error code.
443 for (uint256 const& nftID : nftIDs)
444 {
445 uint256 const offerID =
446 keylet::nftoffer(buyer, env.seq(buyer)).key;
447 env(token::createOffer(buyer, nftID, XRP(100)),
448 txflags(tfSellNFToken));
449 env.close();
450
451 env(token::cancelOffer(buyer, {offerID}));
452 }
453
454 // Verify that all the NFTs are owned by buyer.
455 Json::Value buyerNFTs = [&env, &buyer]() {
456 Json::Value params;
457 params[jss::account] = buyer.human();
458 params[jss::type] = "state";
459 return env.rpc("json", "account_nfts", to_string(params));
460 }();
461
462 BEAST_EXPECT(
463 buyerNFTs[jss::result][jss::account_nfts].size() ==
464 nftIDs.size());
465 for (Json::Value const& ownedNFT :
466 buyerNFTs[jss::result][jss::account_nfts])
467 {
468 uint256 ownedID;
469 BEAST_EXPECT(ownedID.parseHex(
470 ownedNFT[sfNFTokenID.jsonName].asString()));
471 auto const foundIter =
472 std::find(nftIDs.begin(), nftIDs.end(), ownedID);
473
474 // Assuming we find the NFT, erase it so we know it's been
475 // found and can't be found again.
476 if (BEAST_EXPECT(foundIter != nftIDs.end()))
477 nftIDs.erase(foundIter);
478 }
479
480 // All NFTs should now be accounted for, so nftIDs should be
481 // empty.
482 BEAST_EXPECT(nftIDs.empty());
483 };
484
485 // These seeds fill the last 17 entries of the initial page with
486 // equivalent NFTs. The split should keep these together.
487 static std::initializer_list<std::string_view const> const seventeenHi{
488 // These 16 need to be kept together by the implementation.
489 "sp6JS7f14BuwFY8Mw5EYu5z86hKDL", // 0. 0x399187e9
490 "sp6JS7f14BuwFY8Mw5PUAMwc5ygd7", // 1. 0x399187e9
491 "sp6JS7f14BuwFY8Mw5R3xUBcLSeTs", // 2. 0x399187e9
492 "sp6JS7f14BuwFY8Mw5W6oS5sdC3oF", // 3. 0x399187e9
493 "sp6JS7f14BuwFY8Mw5pYc3D9iuLcw", // 4. 0x399187e9
494 "sp6JS7f14BuwFY8Mw5pfGVnhcdp3b", // 5. 0x399187e9
495 "sp6JS7f14BuwFY8Mw6jS6RdEqXqrN", // 6. 0x399187e9
496 "sp6JS7f14BuwFY8Mw6krt6AKbvRXW", // 7. 0x399187e9
497 "sp6JS7f14BuwFY8Mw6mnVBQq7cAN2", // 8. 0x399187e9
498 "sp6JS7f14BuwFY8Mw8ECJxPjmkufQ", // 9. 0x399187e9
499 "sp6JS7f14BuwFY8Mw8asgzcceGWYm", // 10. 0x399187e9
500 "sp6JS7f14BuwFY8MwF6J3FXnPCgL8", // 11. 0x399187e9
501 "sp6JS7f14BuwFY8MwFEud2w5czv5q", // 12. 0x399187e9
502 "sp6JS7f14BuwFY8MwFNxKVqJnx8P5", // 13. 0x399187e9
503 "sp6JS7f14BuwFY8MwFnTCXg3eRidL", // 14. 0x399187e9
504 "sp6JS7f14BuwFY8Mwj47hv1vrDge6", // 15. 0x399187e9
505
506 // These 17 need to be kept together by the implementation.
507 "sp6JS7f14BuwFY8MwjJCwYr9zSfAv", // 16. 0xabb11898
508 "sp6JS7f14BuwFY8MwjYa5yLkgCLuT", // 17. 0xabb11898
509 "sp6JS7f14BuwFY8MwjenxuJ3TH2Bc", // 18. 0xabb11898
510 "sp6JS7f14BuwFY8MwjriN7Ui11NzB", // 19. 0xabb11898
511 "sp6JS7f14BuwFY8Mwk3AuoJNSEo34", // 20. 0xabb11898
512 "sp6JS7f14BuwFY8MwkT36hnRv8hTo", // 21. 0xabb11898
513 "sp6JS7f14BuwFY8MwkTQixEXfi1Cr", // 22. 0xabb11898
514 "sp6JS7f14BuwFY8MwkYJaZM1yTJBF", // 23. 0xabb11898
515 "sp6JS7f14BuwFY8Mwkc4k1uo85qp2", // 24. 0xabb11898
516 "sp6JS7f14BuwFY8Mwkf7cFhF1uuxx", // 25. 0xabb11898
517 "sp6JS7f14BuwFY8MwmCK2un99wb4e", // 26. 0xabb11898
518 "sp6JS7f14BuwFY8MwmETztNHYu2Bx", // 27. 0xabb11898
519 "sp6JS7f14BuwFY8MwmJws9UwRASfR", // 28. 0xabb11898
520 "sp6JS7f14BuwFY8MwoH5PQkGK8tEb", // 29. 0xabb11898
521 "sp6JS7f14BuwFY8MwoVXtP2yCzjJV", // 30. 0xabb11898
522 "sp6JS7f14BuwFY8MwobxRXA9vsTeX", // 31. 0xabb11898
523 "sp6JS7f14BuwFY8Mwos3pc5Gb3ihU", // 32. 0xabb11898
524 };
525
526 // These seeds fill the first entries of the initial page with
527 // equivalent NFTs. The split should keep these together.
528 static std::initializer_list<std::string_view const> const seventeenLo{
529 // These 17 need to be kept together by the implementation.
530 "sp6JS7f14BuwFY8Mw5EYu5z86hKDL", // 0. 0x399187e9
531 "sp6JS7f14BuwFY8Mw5PUAMwc5ygd7", // 1. 0x399187e9
532 "sp6JS7f14BuwFY8Mw5R3xUBcLSeTs", // 2. 0x399187e9
533 "sp6JS7f14BuwFY8Mw5W6oS5sdC3oF", // 3. 0x399187e9
534 "sp6JS7f14BuwFY8Mw5pYc3D9iuLcw", // 4. 0x399187e9
535 "sp6JS7f14BuwFY8Mw5pfGVnhcdp3b", // 5. 0x399187e9
536 "sp6JS7f14BuwFY8Mw6jS6RdEqXqrN", // 6. 0x399187e9
537 "sp6JS7f14BuwFY8Mw6krt6AKbvRXW", // 7. 0x399187e9
538 "sp6JS7f14BuwFY8Mw6mnVBQq7cAN2", // 8. 0x399187e9
539 "sp6JS7f14BuwFY8Mw8ECJxPjmkufQ", // 9. 0x399187e9
540 "sp6JS7f14BuwFY8Mw8asgzcceGWYm", // 10. 0x399187e9
541 "sp6JS7f14BuwFY8MwF6J3FXnPCgL8", // 11. 0x399187e9
542 "sp6JS7f14BuwFY8MwFEud2w5czv5q", // 12. 0x399187e9
543 "sp6JS7f14BuwFY8MwFNxKVqJnx8P5", // 13. 0x399187e9
544 "sp6JS7f14BuwFY8MwFnTCXg3eRidL", // 14. 0x399187e9
545 "sp6JS7f14BuwFY8Mwj47hv1vrDge6", // 15. 0x399187e9
546 "sp6JS7f14BuwFY8Mwj6TYekeeyukh", // 16. 0x399187e9
547
548 // These 16 need to be kept together by the implementation.
549 "sp6JS7f14BuwFY8MwjYa5yLkgCLuT", // 17. 0xabb11898
550 "sp6JS7f14BuwFY8MwjenxuJ3TH2Bc", // 18. 0xabb11898
551 "sp6JS7f14BuwFY8MwjriN7Ui11NzB", // 19. 0xabb11898
552 "sp6JS7f14BuwFY8Mwk3AuoJNSEo34", // 20. 0xabb11898
553 "sp6JS7f14BuwFY8MwkT36hnRv8hTo", // 21. 0xabb11898
554 "sp6JS7f14BuwFY8MwkTQixEXfi1Cr", // 22. 0xabb11898
555 "sp6JS7f14BuwFY8MwkYJaZM1yTJBF", // 23. 0xabb11898
556 "sp6JS7f14BuwFY8Mwkc4k1uo85qp2", // 24. 0xabb11898
557 "sp6JS7f14BuwFY8Mwkf7cFhF1uuxx", // 25. 0xabb11898
558 "sp6JS7f14BuwFY8MwmCK2un99wb4e", // 26. 0xabb11898
559 "sp6JS7f14BuwFY8MwmETztNHYu2Bx", // 27. 0xabb11898
560 "sp6JS7f14BuwFY8MwmJws9UwRASfR", // 28. 0xabb11898
561 "sp6JS7f14BuwFY8MwoH5PQkGK8tEb", // 29. 0xabb11898
562 "sp6JS7f14BuwFY8MwoVXtP2yCzjJV", // 30. 0xabb11898
563 "sp6JS7f14BuwFY8MwobxRXA9vsTeX", // 31. 0xabb11898
564 "sp6JS7f14BuwFY8Mwos3pc5Gb3ihU", // 32. 0xabb11898
565 };
566
567 // Run the test cases.
568 exercise(seventeenHi);
569 exercise(seventeenLo);
570 }
571
572 void
574 {
575 // Exercise the case where 33 NFTs with identical sort
576 // characteristics are owned by the same account.
577 testcase("NFToken too many same");
578
579 using namespace test::jtx;
580
581 Env env{*this, features};
582
583 // Eventually all of the NFTokens will be owned by buyer.
584 Account const buyer{"buyer"};
585 env.fund(XRP(10000), buyer);
586 env.close();
587
588 // Here are 33 seeds that produce identical low 32-bits in their
589 // corresponding AccountIDs.
591 "sp6JS7f14BuwFY8Mw5FnqmbciPvH6", // 0. 0x9a8ebed3
592 "sp6JS7f14BuwFY8Mw5MBGbyMSsXLp", // 1. 0x9a8ebed3
593 "sp6JS7f14BuwFY8Mw5S4PnDyBdKKm", // 2. 0x9a8ebed3
594 "sp6JS7f14BuwFY8Mw6kcXpM2enE35", // 3. 0x9a8ebed3
595 "sp6JS7f14BuwFY8Mw6tuuSMMwyJ44", // 4. 0x9a8ebed3
596 "sp6JS7f14BuwFY8Mw8E8JWLQ1P8pt", // 5. 0x9a8ebed3
597 "sp6JS7f14BuwFY8Mw8WwdgWkCHhEx", // 6. 0x9a8ebed3
598 "sp6JS7f14BuwFY8Mw8XDUYvU6oGhQ", // 7. 0x9a8ebed3
599 "sp6JS7f14BuwFY8Mw8ceVGL4M1zLQ", // 8. 0x9a8ebed3
600 "sp6JS7f14BuwFY8Mw8fdSwLCZWDFd", // 9. 0x9a8ebed3
601 "sp6JS7f14BuwFY8Mw8zuF6Fg65i1E", // 10. 0x9a8ebed3
602 "sp6JS7f14BuwFY8MwF2k7bihVfqes", // 11. 0x9a8ebed3
603 "sp6JS7f14BuwFY8MwF6X24WXGn557", // 12. 0x9a8ebed3
604 "sp6JS7f14BuwFY8MwFMpn7strjekg", // 13. 0x9a8ebed3
605 "sp6JS7f14BuwFY8MwFSdy9sYVrwJs", // 14. 0x9a8ebed3
606 "sp6JS7f14BuwFY8MwFdMcLy9UkrXn", // 15. 0x9a8ebed3
607 "sp6JS7f14BuwFY8MwFdbwFm1AAboa", // 16. 0x9a8ebed3
608 "sp6JS7f14BuwFY8MwFdr5AhKThVtU", // 17. 0x9a8ebed3
609 "sp6JS7f14BuwFY8MwjFc3Q9YatvAw", // 18. 0x9a8ebed3
610 "sp6JS7f14BuwFY8MwjRXcNs1ozEXn", // 19. 0x9a8ebed3
611 "sp6JS7f14BuwFY8MwkQGUKL7v1FBt", // 20. 0x9a8ebed3
612 "sp6JS7f14BuwFY8Mwkamsoxx1wECt", // 21. 0x9a8ebed3
613 "sp6JS7f14BuwFY8Mwm3hus1dG6U8y", // 22. 0x9a8ebed3
614 "sp6JS7f14BuwFY8Mwm589M8vMRpXF", // 23. 0x9a8ebed3
615 "sp6JS7f14BuwFY8MwmJTRJ4Fqz1A3", // 24. 0x9a8ebed3
616 "sp6JS7f14BuwFY8MwmRfy8fer4QbL", // 25. 0x9a8ebed3
617 "sp6JS7f14BuwFY8MwmkkFx1HtgWRx", // 26. 0x9a8ebed3
618 "sp6JS7f14BuwFY8MwmwP9JFdKa4PS", // 27. 0x9a8ebed3
619 "sp6JS7f14BuwFY8MwoXWJLB3ciHfo", // 28. 0x9a8ebed3
620 "sp6JS7f14BuwFY8MwoYc1gTtT2mWL", // 29. 0x9a8ebed3
621 "sp6JS7f14BuwFY8MwogXtHH7FNVoo", // 30. 0x9a8ebed3
622 "sp6JS7f14BuwFY8MwoqYoA9P8gf3r", // 31. 0x9a8ebed3
623 "sp6JS7f14BuwFY8MwoujwMJofGnsA", // 32. 0x9a8ebed3
624 };
625
626 // Create accounts for all of the seeds and fund those accounts.
627 std::vector<Account> accounts;
628 accounts.reserve(seeds.size());
629 for (std::string_view seed : seeds)
630 {
631 Account const& account =
632 accounts.emplace_back(Account::base58Seed, std::string(seed));
633 env.fund(XRP(10000), account);
634
635 // Do not close the ledger inside the loop. If accounts are
636 // initialized at different ledgers, they will have different
637 // account sequences. That would cause the accounts to have
638 // different NFTokenID sequence numbers.
639 }
640 env.close();
641
642 // All of the accounts create one NFT and and offer that NFT to buyer.
645 offers.reserve(accounts.size());
646 for (Account const& account : accounts)
647 {
648 // Mint the NFT.
649 uint256 const& nftID = nftIDs.emplace_back(
650 token::getNextID(env, account, 0, tfTransferable));
651 env(token::mint(account, 0), txflags(tfTransferable));
652 env.close();
653
654 // Create an offer to give the NFT to buyer for free.
655 offers.emplace_back(
656 keylet::nftoffer(account, env.seq(account)).key);
657 env(token::createOffer(account, nftID, XRP(0)),
658 token::destination(buyer),
659 txflags((tfSellNFToken)));
660 }
661 env.close();
662
663 // Verify that the low 96 bits of all generated NFTs is identical.
664 uint256 const expectLowBits = nftIDs.front() & nft::pageMask;
665 for (uint256 const& nftID : nftIDs)
666 {
667 BEAST_EXPECT(expectLowBits == (nftID & nft::pageMask));
668 }
669
670 // Remove one NFT and offer from the vectors. This offer is the one
671 // that will overflow the page.
672 nftIDs.pop_back();
673 uint256 const offerForPageOverflow = offers.back();
674 offers.pop_back();
675
676 // buyer accepts all of the offers but one.
677 for (uint256 const& offer : offers)
678 {
679 env(token::acceptSellOffer(buyer, offer));
680 env.close();
681 }
682
683 // buyer accepts the last offer which causes a page overflow.
684 env(token::acceptSellOffer(buyer, offerForPageOverflow),
686
687 // Verify that all expected NFTs are owned by buyer and findable in
688 // the ledger by having buyer create sell offers for all of their NFTs.
689 // Attempting to sell an offer that the ledger can't find generates
690 // a non-tesSUCCESS error code.
691 for (uint256 const& nftID : nftIDs)
692 {
693 uint256 const offerID = keylet::nftoffer(buyer, env.seq(buyer)).key;
694 env(token::createOffer(buyer, nftID, XRP(100)),
695 txflags(tfSellNFToken));
696 env.close();
697
698 env(token::cancelOffer(buyer, {offerID}));
699 }
700
701 // Verify that all the NFTs are owned by buyer.
702 Json::Value buyerNFTs = [&env, &buyer]() {
703 Json::Value params;
704 params[jss::account] = buyer.human();
705 params[jss::type] = "state";
706 return env.rpc("json", "account_nfts", to_string(params));
707 }();
708
709 BEAST_EXPECT(
710 buyerNFTs[jss::result][jss::account_nfts].size() == nftIDs.size());
711 for (Json::Value const& ownedNFT :
712 buyerNFTs[jss::result][jss::account_nfts])
713 {
714 uint256 ownedID;
715 BEAST_EXPECT(
716 ownedID.parseHex(ownedNFT[sfNFTokenID.jsonName].asString()));
717 auto const foundIter =
718 std::find(nftIDs.begin(), nftIDs.end(), ownedID);
719
720 // Assuming we find the NFT, erase it so we know it's been found
721 // and can't be found again.
722 if (BEAST_EXPECT(foundIter != nftIDs.end()))
723 nftIDs.erase(foundIter);
724 }
725
726 // All NFTs should now be accounted for, so nftIDs should be empty.
727 BEAST_EXPECT(nftIDs.empty());
728
729 TER const expect = tesSUCCESS;
730 env(token::mint(buyer, 0), txflags(tfTransferable), ter(expect));
731 env.close();
732 }
733
734 void
736 {
737 // We'll make a worst case scenario for NFT packing:
738 //
739 // 1. 33 accounts with identical low-32 bits mint 7 consecutive NFTs.
740 // 2. The taxon is manipulated to always be stored as zero.
741 // 3. A single account buys all 7x32 of the 33 NFTs.
742 //
743 // All of the NFTs should be acquired by the buyer.
744 //
745 // Lastly, none of the remaining NFTs should be acquirable by the
746 // buyer. They would cause page overflow.
747
748 testcase("NFToken consecutive packing");
749
750 using namespace test::jtx;
751
752 Env env{*this, features};
753
754 // Eventually all of the NFTokens will be owned by buyer.
755 Account const buyer{"buyer"};
756 env.fund(XRP(10000), buyer);
757 env.close();
758
759 // Here are 33 seeds that produce identical low 32-bits in their
760 // corresponding AccountIDs.
762 "sp6JS7f14BuwFY8Mw56vZeiBuhePx", // 0. 0x115d0525
763 "sp6JS7f14BuwFY8Mw5BodF9tGuTUe", // 1. 0x115d0525
764 "sp6JS7f14BuwFY8Mw5EnhC1cg84J7", // 2. 0x115d0525
765 "sp6JS7f14BuwFY8Mw5P913Cunr2BK", // 3. 0x115d0525
766 "sp6JS7f14BuwFY8Mw5Pru7eLo1XzT", // 4. 0x115d0525
767 "sp6JS7f14BuwFY8Mw61SLUC8UX2m8", // 5. 0x115d0525
768 "sp6JS7f14BuwFY8Mw6AsBF9TpeMpq", // 6. 0x115d0525
769 "sp6JS7f14BuwFY8Mw84XqrBZkU2vE", // 7. 0x115d0525
770 "sp6JS7f14BuwFY8Mw89oSU6dBk3KB", // 8. 0x115d0525
771 "sp6JS7f14BuwFY8Mw89qUKCyDmyzj", // 9. 0x115d0525
772 "sp6JS7f14BuwFY8Mw8GfqQ9VRZ8tm", // 10. 0x115d0525
773 "sp6JS7f14BuwFY8Mw8LtW3VqrqMks", // 11. 0x115d0525
774 "sp6JS7f14BuwFY8Mw8ZrAkJc2sHew", // 12. 0x115d0525
775 "sp6JS7f14BuwFY8Mw8jpkYSNrD3ah", // 13. 0x115d0525
776 "sp6JS7f14BuwFY8MwF2mshd786m3V", // 14. 0x115d0525
777 "sp6JS7f14BuwFY8MwFHfXq9x5NbPY", // 15. 0x115d0525
778 "sp6JS7f14BuwFY8MwFrjWq5LAB8NT", // 16. 0x115d0525
779 "sp6JS7f14BuwFY8Mwj4asgSh6hQZd", // 17. 0x115d0525
780 "sp6JS7f14BuwFY8Mwj7ipFfqBSRrE", // 18. 0x115d0525
781 "sp6JS7f14BuwFY8MwjHqtcvGav8uW", // 19. 0x115d0525
782 "sp6JS7f14BuwFY8MwjLp4sk5fmzki", // 20. 0x115d0525
783 "sp6JS7f14BuwFY8MwjioHuYb3Ytkx", // 21. 0x115d0525
784 "sp6JS7f14BuwFY8MwkRjHPXWi7fGN", // 22. 0x115d0525
785 "sp6JS7f14BuwFY8MwkdVdPV3LjNN1", // 23. 0x115d0525
786 "sp6JS7f14BuwFY8MwkxUtVY5AXZFk", // 24. 0x115d0525
787 "sp6JS7f14BuwFY8Mwm4jQzdfTbY9F", // 25. 0x115d0525
788 "sp6JS7f14BuwFY8MwmCucYAqNp4iF", // 26. 0x115d0525
789 "sp6JS7f14BuwFY8Mwo2bgdFtxBzpF", // 27. 0x115d0525
790 "sp6JS7f14BuwFY8MwoGwD7v4U6qBh", // 28. 0x115d0525
791 "sp6JS7f14BuwFY8MwoUczqFADMoXi", // 29. 0x115d0525
792 "sp6JS7f14BuwFY8MwoY1xZeGd3gAr", // 30. 0x115d0525
793 "sp6JS7f14BuwFY8MwomVCbfkv4kYZ", // 31. 0x115d0525
794 "sp6JS7f14BuwFY8MwoqbrPSr4z13F", // 32. 0x115d0525
795 };
796
797 // Create accounts for all of the seeds and fund those accounts.
798 std::vector<Account> accounts;
799 accounts.reserve(seeds.size());
800 for (std::string_view seed : seeds)
801 {
802 Account const& account =
803 accounts.emplace_back(Account::base58Seed, std::string(seed));
804 env.fund(XRP(10000), account);
805
806 // Do not close the ledger inside the loop. If accounts are
807 // initialized at different ledgers, they will have different
808 // account sequences. That would cause the accounts to have
809 // different NFTokenID sequence numbers.
810 }
811 env.close();
812
813 // All of the accounts create seven consecutive NFTs and and offer
814 // those NFTs to buyer.
815 std::array<std::vector<uint256>, 7> nftIDsByPage;
816 for (auto& vec : nftIDsByPage)
817 vec.reserve(accounts.size());
819 for (auto& vec : offers)
820 vec.reserve(accounts.size());
821 for (std::size_t i = 0; i < nftIDsByPage.size(); ++i)
822 {
823 for (Account const& account : accounts)
824 {
825 // Mint the NFT. Tweak the taxon so zero is always stored.
826 std::uint32_t taxon =
827 toUInt32(nft::cipheredTaxon(i, nft::toTaxon(0)));
828
829 uint256 const& nftID = nftIDsByPage[i].emplace_back(
830 token::getNextID(env, account, taxon, tfTransferable));
831 env(token::mint(account, taxon), txflags(tfTransferable));
832 env.close();
833
834 // Create an offer to give the NFT to buyer for free.
835 offers[i].emplace_back(
836 keylet::nftoffer(account, env.seq(account)).key);
837 env(token::createOffer(account, nftID, XRP(0)),
838 token::destination(buyer),
839 txflags((tfSellNFToken)));
840 }
841 }
842 env.close();
843
844 // Verify that the low 96 bits of all generated NFTs of the same
845 // sequence is identical.
846 for (auto const& vec : nftIDsByPage)
847 {
848 uint256 const expectLowBits = vec.front() & nft::pageMask;
849 for (uint256 const& nftID : vec)
850 {
851 BEAST_EXPECT(expectLowBits == (nftID & nft::pageMask));
852 }
853 }
854
855 // Remove one NFT and offer from each of the vectors. These offers
856 // are the ones that will overflow the page.
857 std::vector<uint256> overflowNFTs;
858 overflowNFTs.reserve(nftIDsByPage.size());
859 std::vector<uint256> overflowOffers;
860 overflowOffers.reserve(nftIDsByPage.size());
861
862 for (std::size_t i = 0; i < nftIDsByPage.size(); ++i)
863 {
864 overflowNFTs.push_back(nftIDsByPage[i].back());
865 nftIDsByPage[i].pop_back();
866 BEAST_EXPECT(nftIDsByPage[i].size() == seeds.size() - 1);
867
868 overflowOffers.push_back(offers[i].back());
869 offers[i].pop_back();
870 BEAST_EXPECT(offers[i].size() == seeds.size() - 1);
871 }
872
873 // buyer accepts all of the offers that won't cause an overflow.
874 // Fill the center and outsides first to exercise different boundary
875 // cases.
876 for (int i : std::initializer_list<int>{3, 6, 0, 1, 2, 5, 4})
877 {
878 for (uint256 const& offer : offers[i])
879 {
880 env(token::acceptSellOffer(buyer, offer));
881 env.close();
882 }
883 }
884
885 // buyer accepts the seven offers that would cause page overflows if
886 // the transaction succeeded.
887 for (uint256 const& offer : overflowOffers)
888 {
889 env(token::acceptSellOffer(buyer, offer),
891 env.close();
892 }
893
894 // Verify that all expected NFTs are owned by buyer and findable in
895 // the ledger by having buyer create sell offers for all of their NFTs.
896 // Attempting to sell an offer that the ledger can't find generates
897 // a non-tesSUCCESS error code.
898 for (auto const& vec : nftIDsByPage)
899 {
900 for (uint256 const& nftID : vec)
901 {
902 env(token::createOffer(buyer, nftID, XRP(100)),
903 txflags(tfSellNFToken));
904 env.close();
905 }
906 }
907
908 // See what the account_objects command does with "nft_offer".
909 {
910 Json::Value ownedNftOffers(Json::arrayValue);
911 std::string marker;
912 do
913 {
914 Json::Value buyerOffers = [&env, &buyer, &marker]() {
915 Json::Value params;
916 params[jss::account] = buyer.human();
917 params[jss::type] = jss::nft_offer;
918
919 if (!marker.empty())
920 params[jss::marker] = marker;
921 return env.rpc(
922 "json", "account_objects", to_string(params));
923 }();
924
925 marker.clear();
926 if (buyerOffers.isMember(jss::result))
927 {
928 Json::Value& result = buyerOffers[jss::result];
929
930 if (result.isMember(jss::marker))
931 marker = result[jss::marker].asString();
932
933 if (result.isMember(jss::account_objects))
934 {
935 Json::Value& someOffers = result[jss::account_objects];
936 for (std::size_t i = 0; i < someOffers.size(); ++i)
937 ownedNftOffers.append(someOffers[i]);
938 }
939 }
940 } while (!marker.empty());
941
942 // Verify there are as many offers are there are NFTs.
943 {
944 std::size_t totalOwnedNFTs = 0;
945 for (auto const& vec : nftIDsByPage)
946 totalOwnedNFTs += vec.size();
947 BEAST_EXPECT(ownedNftOffers.size() == totalOwnedNFTs);
948 }
949
950 // Cancel all the offers.
951 {
952 std::vector<uint256> cancelOffers;
953 cancelOffers.reserve(ownedNftOffers.size());
954
955 for (auto const& offer : ownedNftOffers)
956 {
957 if (offer.isMember(jss::index))
958 {
959 uint256 offerIndex;
960 if (offerIndex.parseHex(offer[jss::index].asString()))
961 cancelOffers.push_back(offerIndex);
962 }
963 }
964 env(token::cancelOffer(buyer, cancelOffers));
965 env.close();
966 }
967
968 // account_objects should no longer return any "nft_offer"s.
969 Json::Value remainingOffers = [&env, &buyer]() {
970 Json::Value params;
971 params[jss::account] = buyer.human();
972 params[jss::type] = jss::nft_offer;
973
974 return env.rpc("json", "account_objects", to_string(params));
975 }();
976 BEAST_EXPECT(
977 remainingOffers.isMember(jss::result) &&
978 remainingOffers[jss::result].isMember(jss::account_objects) &&
979 remainingOffers[jss::result][jss::account_objects].size() == 0);
980 }
981
982 // Verify that the ledger reports all of the NFTs owned by buyer.
983 // Use the account_nfts rpc call to get the values.
984 Json::Value ownedNFTs(Json::arrayValue);
985 std::string marker;
986 do
987 {
988 Json::Value buyerNFTs = [&env, &buyer, &marker]() {
989 Json::Value params;
990 params[jss::account] = buyer.human();
991 params[jss::type] = "state";
992
993 if (!marker.empty())
994 params[jss::marker] = marker;
995 return env.rpc("json", "account_nfts", to_string(params));
996 }();
997
998 marker.clear();
999 if (buyerNFTs.isMember(jss::result))
1000 {
1001 Json::Value& result = buyerNFTs[jss::result];
1002
1003 if (result.isMember(jss::marker))
1004 marker = result[jss::marker].asString();
1005
1006 if (result.isMember(jss::account_nfts))
1007 {
1008 Json::Value& someNFTs = result[jss::account_nfts];
1009 for (std::size_t i = 0; i < someNFTs.size(); ++i)
1010 ownedNFTs.append(someNFTs[i]);
1011 }
1012 }
1013 } while (!marker.empty());
1014
1015 // Copy all of the nftIDs into a set to make validation easier.
1016 std::set<uint256> allNftIDs;
1017 for (auto& vec : nftIDsByPage)
1018 allNftIDs.insert(vec.begin(), vec.end());
1019
1020 BEAST_EXPECT(ownedNFTs.size() == allNftIDs.size());
1021
1022 for (Json::Value const& ownedNFT : ownedNFTs)
1023 {
1024 if (ownedNFT.isMember(sfNFTokenID.jsonName))
1025 {
1026 uint256 ownedID;
1027 BEAST_EXPECT(ownedID.parseHex(
1028 ownedNFT[sfNFTokenID.jsonName].asString()));
1029 auto const foundIter = allNftIDs.find(ownedID);
1030
1031 // Assuming we find the NFT, erase it so we know it's been found
1032 // and can't be found again.
1033 if (BEAST_EXPECT(foundIter != allNftIDs.end()))
1034 allNftIDs.erase(foundIter);
1035 }
1036 }
1037
1038 // All NFTs should now be accounted for, so allNftIDs should be empty.
1039 BEAST_EXPECT(allNftIDs.empty());
1040 }
1041
1042 void
1044 {
1045 testConsecutiveNFTs(features);
1046 testLopsidedSplits(features);
1047 testNFTokenDir(features);
1048 testTooManyEquivalent(features);
1049 testConsecutivePacking(features);
1050 }
1051
1052public:
1053 void
1054 run() override
1055 {
1056 using namespace test::jtx;
1057 FeatureBitset const all{testable_amendments()};
1058
1060 }
1061};
1062
1063BEAST_DEFINE_TESTSUITE_PRIO(NFTokenDir, app, ripple, 1);
1064
1065} // namespace ripple
1066
1067// Seed that produces an account with the low-32 bits == 0xFFFFFFFF in
1068// case it is needed for future testing:
1069//
1070// sp6JS7f14BuwFY8MwFe95Vpi9Znjs
1071//
1072
1073// Sets of related accounts.
1074//
1075// Identifying the seeds of accounts that generate account IDs with the
1076// same low 32 bits takes a while. However several sets of accounts with
1077// that relationship have been located. In case these sets of accounts are
1078// needed for future testing scenarios they are recorded below.
1079#if 0
108034 account seeds that produce account IDs with low 32-bits 0x399187e9:
1081 sp6JS7f14BuwFY8Mw5EYu5z86hKDL
1082 sp6JS7f14BuwFY8Mw5PUAMwc5ygd7
1083 sp6JS7f14BuwFY8Mw5R3xUBcLSeTs
1084 sp6JS7f14BuwFY8Mw5W6oS5sdC3oF
1085 sp6JS7f14BuwFY8Mw5pYc3D9iuLcw
1086 sp6JS7f14BuwFY8Mw5pfGVnhcdp3b
1087 sp6JS7f14BuwFY8Mw6jS6RdEqXqrN
1088 sp6JS7f14BuwFY8Mw6krt6AKbvRXW
1089 sp6JS7f14BuwFY8Mw6mnVBQq7cAN2
1090 sp6JS7f14BuwFY8Mw8ECJxPjmkufQ
1091 sp6JS7f14BuwFY8Mw8asgzcceGWYm
1092 sp6JS7f14BuwFY8MwF6J3FXnPCgL8
1093 sp6JS7f14BuwFY8MwFEud2w5czv5q
1094 sp6JS7f14BuwFY8MwFNxKVqJnx8P5
1095 sp6JS7f14BuwFY8MwFnTCXg3eRidL
1096 sp6JS7f14BuwFY8Mwj47hv1vrDge6
1097 sp6JS7f14BuwFY8Mwj6TYekeeyukh
1098 sp6JS7f14BuwFY8MwjFjsRDerz7jb
1099 sp6JS7f14BuwFY8Mwjrj9mHTLBrcX
1100 sp6JS7f14BuwFY8MwkKcJi3zMzAea
1101 sp6JS7f14BuwFY8MwkYTDdnYRm9z4
1102 sp6JS7f14BuwFY8Mwkq8ei4D8uPNd
1103 sp6JS7f14BuwFY8Mwm2pFruxbnJRd
1104 sp6JS7f14BuwFY8MwmJV2ZnAjpC2g
1105 sp6JS7f14BuwFY8MwmTFMPHQHfVYF
1106 sp6JS7f14BuwFY8MwmkG2jXEgqiud
1107 sp6JS7f14BuwFY8Mwms3xEh5tMDTw
1108 sp6JS7f14BuwFY8MwmtipW4D8giZ9
1109 sp6JS7f14BuwFY8MwoRQBZm4KUUeE
1110 sp6JS7f14BuwFY8MwoVey94QpXcrc
1111 sp6JS7f14BuwFY8MwoZiuUoUTo3VG
1112 sp6JS7f14BuwFY8MwonFFDLT4bHAZ
1113 sp6JS7f14BuwFY8MwooGphD4hefBQ
1114 sp6JS7f14BuwFY8MwoxDp3dmX6q5N
1115
111634 account seeds that produce account IDs with low 32-bits 0x473f2c9a:
1117 sp6JS7f14BuwFY8Mw53ktgqmv5Bmz
1118 sp6JS7f14BuwFY8Mw5KPb2Kz7APFX
1119 sp6JS7f14BuwFY8Mw5Xx4A6HRTPEE
1120 sp6JS7f14BuwFY8Mw5y6qZFNAo358
1121 sp6JS7f14BuwFY8Mw6kdaBg1QrZfn
1122 sp6JS7f14BuwFY8Mw8QmTfLMAZ5K1
1123 sp6JS7f14BuwFY8Mw8cbRRVcCEELr
1124 sp6JS7f14BuwFY8Mw8gQvJebmxvDG
1125 sp6JS7f14BuwFY8Mw8qPQurwu3P7Y
1126 sp6JS7f14BuwFY8MwFS4PEVKmuPy5
1127 sp6JS7f14BuwFY8MwFUQM1rAsQ8tS
1128 sp6JS7f14BuwFY8MwjJBZCkuwsRnM
1129 sp6JS7f14BuwFY8MwjTdS8vZhX5E9
1130 sp6JS7f14BuwFY8MwjhSmWCbNhd25
1131 sp6JS7f14BuwFY8MwjwkpqwZsDBw9
1132 sp6JS7f14BuwFY8MwjyET4p6eqd5J
1133 sp6JS7f14BuwFY8MwkMNAe4JhnG7E
1134 sp6JS7f14BuwFY8MwkRRpnT93UWWS
1135 sp6JS7f14BuwFY8MwkY9CvB22RvUe
1136 sp6JS7f14BuwFY8Mwkhw9VxXqmTr7
1137 sp6JS7f14BuwFY8MwkmgaTat7eFa7
1138 sp6JS7f14BuwFY8Mwkq5SxGGv1oLH
1139 sp6JS7f14BuwFY8MwmCBM5p5bTg6y
1140 sp6JS7f14BuwFY8MwmmmXaVah64dB
1141 sp6JS7f14BuwFY8Mwo7R7Cn614v9V
1142 sp6JS7f14BuwFY8MwoCAG1na7GR2M
1143 sp6JS7f14BuwFY8MwoDuPvJS4gG7C
1144 sp6JS7f14BuwFY8MwoMMowSyPQLfy
1145 sp6JS7f14BuwFY8MwoRqDiwTNsTBm
1146 sp6JS7f14BuwFY8MwoWbBWtjpB7pg
1147 sp6JS7f14BuwFY8Mwoi1AEeELGecF
1148 sp6JS7f14BuwFY8MwopGP6Lo5byuj
1149 sp6JS7f14BuwFY8MwoufkXGHp2VW8
1150 sp6JS7f14BuwFY8MwowGeagFQY32k
1151
115234 account seeds that produce account IDs with low 32-bits 0x4d59f0d1:
1153 sp6JS7f14BuwFY8Mw5CsNgH64zxK7
1154 sp6JS7f14BuwFY8Mw5Dg4wi2E344h
1155 sp6JS7f14BuwFY8Mw5ErV949Zh2PX
1156 sp6JS7f14BuwFY8Mw5p4nsQvEUE1s
1157 sp6JS7f14BuwFY8Mw8LGnkbaP68Gn
1158 sp6JS7f14BuwFY8Mw8aq6RCBc3iHo
1159 sp6JS7f14BuwFY8Mw8bkWaGoKYT6e
1160 sp6JS7f14BuwFY8Mw8qrCuXnzAXVj
1161 sp6JS7f14BuwFY8MwFDKcPAHPHJTm
1162 sp6JS7f14BuwFY8MwFUXJs4unfgNu
1163 sp6JS7f14BuwFY8MwFj9Yv5LjshD9
1164 sp6JS7f14BuwFY8Mwj3H73nmq5UaC
1165 sp6JS7f14BuwFY8MwjHSYShis1Yhk
1166 sp6JS7f14BuwFY8MwjpfE1HVo8UP1
1167 sp6JS7f14BuwFY8Mwk6JE1SXUuiNc
1168 sp6JS7f14BuwFY8MwkASgxEjEnFmU
1169 sp6JS7f14BuwFY8MwkGNY8kg7R6RK
1170 sp6JS7f14BuwFY8MwkHinNZ8SYBQu
1171 sp6JS7f14BuwFY8MwkXLCW1hbhGya
1172 sp6JS7f14BuwFY8MwkZ7mWrYK9YtU
1173 sp6JS7f14BuwFY8MwkdFSqNB5DbKL
1174 sp6JS7f14BuwFY8Mwm3jdBaCAx8H6
1175 sp6JS7f14BuwFY8Mwm3rk5hEwDRtY
1176 sp6JS7f14BuwFY8Mwm77a2ULuwxu4
1177 sp6JS7f14BuwFY8MwmJpY7braKLaN
1178 sp6JS7f14BuwFY8MwmKHQjG4XiZ6g
1179 sp6JS7f14BuwFY8Mwmmv8Y3wyUDzs
1180 sp6JS7f14BuwFY8MwmucFe1WgqtwG
1181 sp6JS7f14BuwFY8Mwo1EjdU1bznZR
1182 sp6JS7f14BuwFY8MwoJiqankkU5uR
1183 sp6JS7f14BuwFY8MwoLnvQ6zdqbKw
1184 sp6JS7f14BuwFY8MwoUGeJ319eu48
1185 sp6JS7f14BuwFY8MwoYf135tQjHP4
1186 sp6JS7f14BuwFY8MwogeF6M6SAyid
1187
118834 account seeds that produce account IDs with low 32-bits 0xabb11898:
1189 sp6JS7f14BuwFY8Mw5DgiYaNVSb1G
1190 sp6JS7f14BuwFY8Mw5k6e94TMvuox
1191 sp6JS7f14BuwFY8Mw5tTSN7KzYxiT
1192 sp6JS7f14BuwFY8Mw61XV6m33utif
1193 sp6JS7f14BuwFY8Mw87jKfrjiENCb
1194 sp6JS7f14BuwFY8Mw8AFtxxFiRtJG
1195 sp6JS7f14BuwFY8Mw8cosAVExzbeE
1196 sp6JS7f14BuwFY8Mw8fmkQ63zE8WQ
1197 sp6JS7f14BuwFY8Mw8iYSsxNbDN6D
1198 sp6JS7f14BuwFY8Mw8wTZdGRJyyM1
1199 sp6JS7f14BuwFY8Mw8z7xEh3qBGr7
1200 sp6JS7f14BuwFY8MwFL5gpKQWZj7g
1201 sp6JS7f14BuwFY8MwFPeZchXQnRZ5
1202 sp6JS7f14BuwFY8MwFSPxWSJVoU29
1203 sp6JS7f14BuwFY8MwFYyVkqX8kvRm
1204 sp6JS7f14BuwFY8MwFcbVikUEwJvk
1205 sp6JS7f14BuwFY8MwjF7NcZk1NctK
1206 sp6JS7f14BuwFY8MwjJCwYr9zSfAv
1207 sp6JS7f14BuwFY8MwjYa5yLkgCLuT
1208 sp6JS7f14BuwFY8MwjenxuJ3TH2Bc
1209 sp6JS7f14BuwFY8MwjriN7Ui11NzB
1210 sp6JS7f14BuwFY8Mwk3AuoJNSEo34
1211 sp6JS7f14BuwFY8MwkT36hnRv8hTo
1212 sp6JS7f14BuwFY8MwkTQixEXfi1Cr
1213 sp6JS7f14BuwFY8MwkYJaZM1yTJBF
1214 sp6JS7f14BuwFY8Mwkc4k1uo85qp2
1215 sp6JS7f14BuwFY8Mwkf7cFhF1uuxx
1216 sp6JS7f14BuwFY8MwmCK2un99wb4e
1217 sp6JS7f14BuwFY8MwmETztNHYu2Bx
1218 sp6JS7f14BuwFY8MwmJws9UwRASfR
1219 sp6JS7f14BuwFY8MwoH5PQkGK8tEb
1220 sp6JS7f14BuwFY8MwoVXtP2yCzjJV
1221 sp6JS7f14BuwFY8MwobxRXA9vsTeX
1222 sp6JS7f14BuwFY8Mwos3pc5Gb3ihU
1223
122434 account seeds that produce account IDs with low 32-bits 0xce627322:
1225 sp6JS7f14BuwFY8Mw5Ck6i83pGNh3
1226 sp6JS7f14BuwFY8Mw5FKuwTxjAdH1
1227 sp6JS7f14BuwFY8Mw5FVKkEn6TkLH
1228 sp6JS7f14BuwFY8Mw5NbQwLwHDd5v
1229 sp6JS7f14BuwFY8Mw5X1dbz3msZaZ
1230 sp6JS7f14BuwFY8Mw6qv6qaXNeP74
1231 sp6JS7f14BuwFY8Mw81SXagUeutCw
1232 sp6JS7f14BuwFY8Mw84Ph7Qa8kwwk
1233 sp6JS7f14BuwFY8Mw8Hp4gFyU3Qko
1234 sp6JS7f14BuwFY8Mw8Kt8bAKredSx
1235 sp6JS7f14BuwFY8Mw8XHK3VKRQ7v7
1236 sp6JS7f14BuwFY8Mw8eGyWxZGHY6v
1237 sp6JS7f14BuwFY8Mw8iU5CLyHVcD2
1238 sp6JS7f14BuwFY8Mw8u3Zr26Ar914
1239 sp6JS7f14BuwFY8MwF2Kcdxtjzjv8
1240 sp6JS7f14BuwFY8MwFLmPWb6rbxNg
1241 sp6JS7f14BuwFY8MwFUu8s7UVuxuJ
1242 sp6JS7f14BuwFY8MwFYBaatwHxAJ8
1243 sp6JS7f14BuwFY8Mwjg6hFkeHwoqG
1244 sp6JS7f14BuwFY8MwjjycJojy2ufk
1245 sp6JS7f14BuwFY8MwkEWoxcSKGPXv
1246 sp6JS7f14BuwFY8MwkMe7wLkEUsQT
1247 sp6JS7f14BuwFY8MwkvyKLaPUc4FS
1248 sp6JS7f14BuwFY8Mwm8doqXPKZmVQ
1249 sp6JS7f14BuwFY8Mwm9r3No8yQ8Tx
1250 sp6JS7f14BuwFY8Mwm9w6dks68W9B
1251 sp6JS7f14BuwFY8MwmMPrv9sCdbpS
1252 sp6JS7f14BuwFY8MwmPAvs3fcQNja
1253 sp6JS7f14BuwFY8MwmS5jasapfcnJ
1254 sp6JS7f14BuwFY8MwmU2L3qJEhnuA
1255 sp6JS7f14BuwFY8MwoAQYmiBnW7fM
1256 sp6JS7f14BuwFY8MwoBkkkXrPmkKF
1257 sp6JS7f14BuwFY8MwonfmxPo6tkvC
1258 sp6JS7f14BuwFY8MwouZFwhiNcYq6
1259
126034 account seeds that produce account IDs with low 32-bits 0xe29643e8:
1261 sp6JS7f14BuwFY8Mw5EfAavcXAh2k
1262 sp6JS7f14BuwFY8Mw5LhFjLkFSCVF
1263 sp6JS7f14BuwFY8Mw5bRfEv5HgdBh
1264 sp6JS7f14BuwFY8Mw5d6sPcKzypKN
1265 sp6JS7f14BuwFY8Mw5rcqDtk1fACP
1266 sp6JS7f14BuwFY8Mw5xkxRq1Notzv
1267 sp6JS7f14BuwFY8Mw66fbkdw5WYmt
1268 sp6JS7f14BuwFY8Mw6diEG8sZ7Fx7
1269 sp6JS7f14BuwFY8Mw6v2r1QhG7xc1
1270 sp6JS7f14BuwFY8Mw6zP6DHCTx2Fd
1271 sp6JS7f14BuwFY8Mw8B3n39JKuFkk
1272 sp6JS7f14BuwFY8Mw8FmBvqYw7uqn
1273 sp6JS7f14BuwFY8Mw8KEaftb1eRwu
1274 sp6JS7f14BuwFY8Mw8WJ1qKkegj9N
1275 sp6JS7f14BuwFY8Mw8r8cAZEkq2BS
1276 sp6JS7f14BuwFY8MwFKPxxwF65gZh
1277 sp6JS7f14BuwFY8MwFKhaF8APcN5H
1278 sp6JS7f14BuwFY8MwFN2buJn4BgYC
1279 sp6JS7f14BuwFY8MwFUTe175MjP3x
1280 sp6JS7f14BuwFY8MwFZhmRDb53NNb
1281 sp6JS7f14BuwFY8MwFa2Azn5nU2WS
1282 sp6JS7f14BuwFY8MwjNNt91hwgkn7
1283 sp6JS7f14BuwFY8MwjdiYt6ChACe7
1284 sp6JS7f14BuwFY8Mwk5qFVQ48Mmr9
1285 sp6JS7f14BuwFY8MwkGvCj7pNf1zG
1286 sp6JS7f14BuwFY8MwkY9UcN2D2Fzs
1287 sp6JS7f14BuwFY8MwkpGvSk9G9RyT
1288 sp6JS7f14BuwFY8MwmGQ7nJf1eEzV
1289 sp6JS7f14BuwFY8MwmQLjGsYdyAmV
1290 sp6JS7f14BuwFY8MwmZ8usztKvikT
1291 sp6JS7f14BuwFY8MwobyMLC2hQdFR
1292 sp6JS7f14BuwFY8MwoiRtwUecZeJ5
1293 sp6JS7f14BuwFY8MwojHjKsUzj1KJ
1294 sp6JS7f14BuwFY8Mwop29anGAjidU
1295
129633 account seeds that produce account IDs with low 32-bits 0x115d0525:
1297 sp6JS7f14BuwFY8Mw56vZeiBuhePx
1298 sp6JS7f14BuwFY8Mw5BodF9tGuTUe
1299 sp6JS7f14BuwFY8Mw5EnhC1cg84J7
1300 sp6JS7f14BuwFY8Mw5P913Cunr2BK
1301 sp6JS7f14BuwFY8Mw5Pru7eLo1XzT
1302 sp6JS7f14BuwFY8Mw61SLUC8UX2m8
1303 sp6JS7f14BuwFY8Mw6AsBF9TpeMpq
1304 sp6JS7f14BuwFY8Mw84XqrBZkU2vE
1305 sp6JS7f14BuwFY8Mw89oSU6dBk3KB
1306 sp6JS7f14BuwFY8Mw89qUKCyDmyzj
1307 sp6JS7f14BuwFY8Mw8GfqQ9VRZ8tm
1308 sp6JS7f14BuwFY8Mw8LtW3VqrqMks
1309 sp6JS7f14BuwFY8Mw8ZrAkJc2sHew
1310 sp6JS7f14BuwFY8Mw8jpkYSNrD3ah
1311 sp6JS7f14BuwFY8MwF2mshd786m3V
1312 sp6JS7f14BuwFY8MwFHfXq9x5NbPY
1313 sp6JS7f14BuwFY8MwFrjWq5LAB8NT
1314 sp6JS7f14BuwFY8Mwj4asgSh6hQZd
1315 sp6JS7f14BuwFY8Mwj7ipFfqBSRrE
1316 sp6JS7f14BuwFY8MwjHqtcvGav8uW
1317 sp6JS7f14BuwFY8MwjLp4sk5fmzki
1318 sp6JS7f14BuwFY8MwjioHuYb3Ytkx
1319 sp6JS7f14BuwFY8MwkRjHPXWi7fGN
1320 sp6JS7f14BuwFY8MwkdVdPV3LjNN1
1321 sp6JS7f14BuwFY8MwkxUtVY5AXZFk
1322 sp6JS7f14BuwFY8Mwm4jQzdfTbY9F
1323 sp6JS7f14BuwFY8MwmCucYAqNp4iF
1324 sp6JS7f14BuwFY8Mwo2bgdFtxBzpF
1325 sp6JS7f14BuwFY8MwoGwD7v4U6qBh
1326 sp6JS7f14BuwFY8MwoUczqFADMoXi
1327 sp6JS7f14BuwFY8MwoY1xZeGd3gAr
1328 sp6JS7f14BuwFY8MwomVCbfkv4kYZ
1329 sp6JS7f14BuwFY8MwoqbrPSr4z13F
1330
133133 account seeds that produce account IDs with low 32-bits 0x304033aa:
1332 sp6JS7f14BuwFY8Mw5DaUP9agF5e1
1333 sp6JS7f14BuwFY8Mw5ohbtmPN4yGN
1334 sp6JS7f14BuwFY8Mw5rRsA5fcoTAQ
1335 sp6JS7f14BuwFY8Mw6zpYHMY3m6KT
1336 sp6JS7f14BuwFY8Mw86BzQq4sTnoW
1337 sp6JS7f14BuwFY8Mw8CCpnfvmGdV7
1338 sp6JS7f14BuwFY8Mw8DRjUDaBcFco
1339 sp6JS7f14BuwFY8Mw8cL7GPo3zZN7
1340 sp6JS7f14BuwFY8Mw8y6aeYVtH6qt
1341 sp6JS7f14BuwFY8MwFZR3PtVTCdUH
1342 sp6JS7f14BuwFY8MwFcdcdbgz7m3s
1343 sp6JS7f14BuwFY8MwjdnJDiUxEBRR
1344 sp6JS7f14BuwFY8MwjhxWgSntqrFe
1345 sp6JS7f14BuwFY8MwjrSHEhZ8CUM1
1346 sp6JS7f14BuwFY8MwjzkEeSTc9ZYf
1347 sp6JS7f14BuwFY8MwkBZSk9JhaeCB
1348 sp6JS7f14BuwFY8MwkGfwNY4i2iiU
1349 sp6JS7f14BuwFY8MwknjtZd2oU2Ff
1350 sp6JS7f14BuwFY8Mwkszsqd3ok9NE
1351 sp6JS7f14BuwFY8Mwm58A81MAMvgZ
1352 sp6JS7f14BuwFY8MwmiPTWysuDJCH
1353 sp6JS7f14BuwFY8MwmxhiNeLfD76r
1354 sp6JS7f14BuwFY8Mwo7SPdkwpGrFH
1355 sp6JS7f14BuwFY8MwoANq4F1Sj3qH
1356 sp6JS7f14BuwFY8MwoVjcHufAkd6L
1357 sp6JS7f14BuwFY8MwoVxHBXdaxzhm
1358 sp6JS7f14BuwFY8MwoZ2oTjBNfLpm
1359 sp6JS7f14BuwFY8Mwoc9swzyotFVD
1360 sp6JS7f14BuwFY8MwogMqVRwVEcQ9
1361 sp6JS7f14BuwFY8MwohMm7WxwnFqH
1362 sp6JS7f14BuwFY8MwopUcpZHuF8BH
1363 sp6JS7f14BuwFY8Mwor6rW6SS7tiB
1364 sp6JS7f14BuwFY8MwoxyaqYz4Ngsb
1365
136633 account seeds that produce account IDs with low 32-bits 0x42d4e09c:
1367 sp6JS7f14BuwFY8Mw58NSZH9EaUxQ
1368 sp6JS7f14BuwFY8Mw5JByk1pgPpL7
1369 sp6JS7f14BuwFY8Mw5YrJJuXnkHVB
1370 sp6JS7f14BuwFY8Mw5kZe2ZzNSnKR
1371 sp6JS7f14BuwFY8Mw6eXHTsbwi1U7
1372 sp6JS7f14BuwFY8Mw6gqN7HHDDKSh
1373 sp6JS7f14BuwFY8Mw6zw8L1sSSR53
1374 sp6JS7f14BuwFY8Mw8E4WqSKKbksy
1375 sp6JS7f14BuwFY8MwF3V9gemqJtND
1376 sp6JS7f14BuwFY8Mwj4j46LHWZuY6
1377 sp6JS7f14BuwFY8MwjF5i8vh4Ezjy
1378 sp6JS7f14BuwFY8MwjJZpEKgMpUAt
1379 sp6JS7f14BuwFY8MwjWL7LfnzNUuh
1380 sp6JS7f14BuwFY8Mwk7Y1csGuqAhX
1381 sp6JS7f14BuwFY8MwkB1HVH17hN5W
1382 sp6JS7f14BuwFY8MwkBntH7BZZupu
1383 sp6JS7f14BuwFY8MwkEy4rMbNHG9P
1384 sp6JS7f14BuwFY8MwkKz4LYesZeiN
1385 sp6JS7f14BuwFY8MwkUrXyo9gMDPM
1386 sp6JS7f14BuwFY8MwkV2hySsxej1G
1387 sp6JS7f14BuwFY8MwkozhTVN12F9C
1388 sp6JS7f14BuwFY8MwkpkzGB3sFJw5
1389 sp6JS7f14BuwFY8Mwks3zDZLGrhdn
1390 sp6JS7f14BuwFY8MwktG1KCS7L2wW
1391 sp6JS7f14BuwFY8Mwm1jVFsafwcYx
1392 sp6JS7f14BuwFY8Mwm8hmrU6g5Wd6
1393 sp6JS7f14BuwFY8MwmFvstfRF7e2f
1394 sp6JS7f14BuwFY8MwmeRohi6m5fs8
1395 sp6JS7f14BuwFY8MwmmU96RHUaRZL
1396 sp6JS7f14BuwFY8MwoDFzteYqaUh4
1397 sp6JS7f14BuwFY8MwoPkTf5tDykPF
1398 sp6JS7f14BuwFY8MwoSbMaDtiMoDN
1399 sp6JS7f14BuwFY8MwoVL1vY1CysjR
1400
140133 account seeds that produce account IDs with low 32-bits 0x9a8ebed3:
1402 sp6JS7f14BuwFY8Mw5FnqmbciPvH6
1403 sp6JS7f14BuwFY8Mw5MBGbyMSsXLp
1404 sp6JS7f14BuwFY8Mw5S4PnDyBdKKm
1405 sp6JS7f14BuwFY8Mw6kcXpM2enE35
1406 sp6JS7f14BuwFY8Mw6tuuSMMwyJ44
1407 sp6JS7f14BuwFY8Mw8E8JWLQ1P8pt
1408 sp6JS7f14BuwFY8Mw8WwdgWkCHhEx
1409 sp6JS7f14BuwFY8Mw8XDUYvU6oGhQ
1410 sp6JS7f14BuwFY8Mw8ceVGL4M1zLQ
1411 sp6JS7f14BuwFY8Mw8fdSwLCZWDFd
1412 sp6JS7f14BuwFY8Mw8zuF6Fg65i1E
1413 sp6JS7f14BuwFY8MwF2k7bihVfqes
1414 sp6JS7f14BuwFY8MwF6X24WXGn557
1415 sp6JS7f14BuwFY8MwFMpn7strjekg
1416 sp6JS7f14BuwFY8MwFSdy9sYVrwJs
1417 sp6JS7f14BuwFY8MwFdMcLy9UkrXn
1418 sp6JS7f14BuwFY8MwFdbwFm1AAboa
1419 sp6JS7f14BuwFY8MwFdr5AhKThVtU
1420 sp6JS7f14BuwFY8MwjFc3Q9YatvAw
1421 sp6JS7f14BuwFY8MwjRXcNs1ozEXn
1422 sp6JS7f14BuwFY8MwkQGUKL7v1FBt
1423 sp6JS7f14BuwFY8Mwkamsoxx1wECt
1424 sp6JS7f14BuwFY8Mwm3hus1dG6U8y
1425 sp6JS7f14BuwFY8Mwm589M8vMRpXF
1426 sp6JS7f14BuwFY8MwmJTRJ4Fqz1A3
1427 sp6JS7f14BuwFY8MwmRfy8fer4QbL
1428 sp6JS7f14BuwFY8MwmkkFx1HtgWRx
1429 sp6JS7f14BuwFY8MwmwP9JFdKa4PS
1430 sp6JS7f14BuwFY8MwoXWJLB3ciHfo
1431 sp6JS7f14BuwFY8MwoYc1gTtT2mWL
1432 sp6JS7f14BuwFY8MwogXtHH7FNVoo
1433 sp6JS7f14BuwFY8MwoqYoA9P8gf3r
1434 sp6JS7f14BuwFY8MwoujwMJofGnsA
1435
143633 account seeds that produce account IDs with low 32-bits 0xa1dcea4a:
1437 sp6JS7f14BuwFY8Mw5Ccov2N36QTy
1438 sp6JS7f14BuwFY8Mw5CuSemVb5p7w
1439 sp6JS7f14BuwFY8Mw5Ep8wpsTfpSz
1440 sp6JS7f14BuwFY8Mw5WtutJc2H45M
1441 sp6JS7f14BuwFY8Mw6vsDeaSKeUJZ
1442 sp6JS7f14BuwFY8Mw83t5BPWUAzzF
1443 sp6JS7f14BuwFY8Mw8FYGnK35mgkV
1444 sp6JS7f14BuwFY8Mw8huo1x5pfKKJ
1445 sp6JS7f14BuwFY8Mw8mPStxfMDrZa
1446 sp6JS7f14BuwFY8Mw8yC3A7aQJytK
1447 sp6JS7f14BuwFY8MwFCWCDmo9o3t8
1448 sp6JS7f14BuwFY8MwFjapa4gKxPhR
1449 sp6JS7f14BuwFY8Mwj8CWtG29uw71
1450 sp6JS7f14BuwFY8MwjHyU5KpEMLVT
1451 sp6JS7f14BuwFY8MwjMZSN7LZuWD8
1452 sp6JS7f14BuwFY8Mwja2TXJNBhKHU
1453 sp6JS7f14BuwFY8Mwjf3xNTopHKTF
1454 sp6JS7f14BuwFY8Mwjn5RAhedPeuM
1455 sp6JS7f14BuwFY8MwkJdr4d6QoE8K
1456 sp6JS7f14BuwFY8MwkmBryo3SUoLm
1457 sp6JS7f14BuwFY8MwkrPdsc4tR8yw
1458 sp6JS7f14BuwFY8Mwkttjcw2a65Fi
1459 sp6JS7f14BuwFY8Mwm19n3rSaNx5S
1460 sp6JS7f14BuwFY8Mwm3ryr4Xp2aQX
1461 sp6JS7f14BuwFY8MwmBnDmgnJLB6B
1462 sp6JS7f14BuwFY8MwmHgPjzrYjthq
1463 sp6JS7f14BuwFY8MwmeV55DAnWKdd
1464 sp6JS7f14BuwFY8Mwo49hK6BGrauT
1465 sp6JS7f14BuwFY8Mwo56vfKY9aoWu
1466 sp6JS7f14BuwFY8MwoU7tTTXLQTrh
1467 sp6JS7f14BuwFY8MwoXpogSF2KaZB
1468 sp6JS7f14BuwFY8MwoY9JYQAR16pc
1469 sp6JS7f14BuwFY8MwoozLzKNAEXKM
1470
147133 account seeds that produce account IDs with low 32-bits 0xbd2116db:
1472 sp6JS7f14BuwFY8Mw5GrpkmPuA3Bw
1473 sp6JS7f14BuwFY8Mw5r1sLoQJZDc6
1474 sp6JS7f14BuwFY8Mw68zzRmezLdd6
1475 sp6JS7f14BuwFY8Mw6jDSyaiF1mRp
1476 sp6JS7f14BuwFY8Mw813wU9u5D6Uh
1477 sp6JS7f14BuwFY8Mw8BBvpf2JFGoJ
1478 sp6JS7f14BuwFY8Mw8F7zXxAiT263
1479 sp6JS7f14BuwFY8Mw8XG7WuVGHP2N
1480 sp6JS7f14BuwFY8Mw8eyWrcz91cz6
1481 sp6JS7f14BuwFY8Mw8yNVKFVYyk9u
1482 sp6JS7f14BuwFY8MwF2oA6ePqvZWP
1483 sp6JS7f14BuwFY8MwF9VkcSNh3keq
1484 sp6JS7f14BuwFY8MwFYsMWajgEf2j
1485 sp6JS7f14BuwFY8Mwj3Gu43jYoJ4n
1486 sp6JS7f14BuwFY8MwjJ5iRmYDHrW4
1487 sp6JS7f14BuwFY8MwjaUSSga93CiM
1488 sp6JS7f14BuwFY8MwjxgLh2FY4Lvt
1489 sp6JS7f14BuwFY8Mwk9hQdNZUgmTB
1490 sp6JS7f14BuwFY8MwkcMXqtFp1sMx
1491 sp6JS7f14BuwFY8MwkzZCDc56jsUB
1492 sp6JS7f14BuwFY8Mwm5Zz7fP24Qym
1493 sp6JS7f14BuwFY8MwmDWqizXSoJRG
1494 sp6JS7f14BuwFY8MwmKHmkNYdMqqi
1495 sp6JS7f14BuwFY8MwmRfAWHxWpGNK
1496 sp6JS7f14BuwFY8MwmjCdXwyhphZ1
1497 sp6JS7f14BuwFY8MwmmukDAm1w6FL
1498 sp6JS7f14BuwFY8Mwmmz2SzaR9TRH
1499 sp6JS7f14BuwFY8Mwmz2z5mKHXzfn
1500 sp6JS7f14BuwFY8Mwo2xNe5629r5k
1501 sp6JS7f14BuwFY8MwoKy8tZxZrfJw
1502 sp6JS7f14BuwFY8MwoLyQ9aMsq8Dm
1503 sp6JS7f14BuwFY8MwoqqYkewuyZck
1504 sp6JS7f14BuwFY8MwouvvhREVp6Pp
1505
150633 account seeds that produce account IDs with low 32-bits 0xd80df065:
1507 sp6JS7f14BuwFY8Mw5B7ERyhAfgHA
1508 sp6JS7f14BuwFY8Mw5VuW3cF7bm2v
1509 sp6JS7f14BuwFY8Mw5py3t1j7YbFT
1510 sp6JS7f14BuwFY8Mw5qc84SzB6RHr
1511 sp6JS7f14BuwFY8Mw5vGHW1G1hAy8
1512 sp6JS7f14BuwFY8Mw6gVa8TYukws6
1513 sp6JS7f14BuwFY8Mw8K9w1RoUAv1w
1514 sp6JS7f14BuwFY8Mw8KvKtB7787CA
1515 sp6JS7f14BuwFY8Mw8Y7WhRbuFzRq
1516 sp6JS7f14BuwFY8Mw8cipw7inRmMn
1517 sp6JS7f14BuwFY8MwFM5fAUNLNB13
1518 sp6JS7f14BuwFY8MwFSe1zAsht3X3
1519 sp6JS7f14BuwFY8MwFYNdigqQuHZM
1520 sp6JS7f14BuwFY8MwjWkejj7V4V5Q
1521 sp6JS7f14BuwFY8Mwjd2JGpsjvynq
1522 sp6JS7f14BuwFY8Mwjg1xkducn751
1523 sp6JS7f14BuwFY8Mwjsp6LnaJvL1W
1524 sp6JS7f14BuwFY8MwjvSbLc9593yH
1525 sp6JS7f14BuwFY8Mwjw2h5wx7U6vZ
1526 sp6JS7f14BuwFY8MwjxKUjtRsmPLH
1527 sp6JS7f14BuwFY8Mwk1Yy8ginDfqv
1528 sp6JS7f14BuwFY8Mwk2HrWhWwZP12
1529 sp6JS7f14BuwFY8Mwk4SsqiexvpWs
1530 sp6JS7f14BuwFY8Mwk66zCs5ACpE6
1531 sp6JS7f14BuwFY8MwkCwx6vY97Nwh
1532 sp6JS7f14BuwFY8MwknrbjnhTTWU8
1533 sp6JS7f14BuwFY8MwkokDy2ShRzQx
1534 sp6JS7f14BuwFY8Mwm3BxnRPNxsuu
1535 sp6JS7f14BuwFY8MwmY9EWdQQsFVr
1536 sp6JS7f14BuwFY8MwmYTWjrDhmk8S
1537 sp6JS7f14BuwFY8Mwo9skXt9Y5BVS
1538 sp6JS7f14BuwFY8MwoZYKZybJ1Crp
1539 sp6JS7f14BuwFY8MwoyXqkhySfSmF
1540
154133 account seeds that produce account IDs with low 32-bits 0xe2e44294:
1542 sp6JS7f14BuwFY8Mw53dmvTgNtBwi
1543 sp6JS7f14BuwFY8Mw5Wrxsqn6WrXW
1544 sp6JS7f14BuwFY8Mw5fGDT31RCXgC
1545 sp6JS7f14BuwFY8Mw5nKRkubwrLWM
1546 sp6JS7f14BuwFY8Mw5nXMajwKjriB
1547 sp6JS7f14BuwFY8Mw5xZybggrC9NG
1548 sp6JS7f14BuwFY8Mw5xea8f6dBMV5
1549 sp6JS7f14BuwFY8Mw5zDGofAHy5Lb
1550 sp6JS7f14BuwFY8Mw6eado41rQNVG
1551 sp6JS7f14BuwFY8Mw6yqKXQsQJPuU
1552 sp6JS7f14BuwFY8Mw83MSN4FDzSGH
1553 sp6JS7f14BuwFY8Mw8B3pUbzQqHe2
1554 sp6JS7f14BuwFY8Mw8WwRLnhBRvfk
1555 sp6JS7f14BuwFY8Mw8hDBpKbpJwJX
1556 sp6JS7f14BuwFY8Mw8jggRSZACe7M
1557 sp6JS7f14BuwFY8Mw8mJRpU3qWbwC
1558 sp6JS7f14BuwFY8MwFDnVozykN21u
1559 sp6JS7f14BuwFY8MwFGGRGY9fctgv
1560 sp6JS7f14BuwFY8MwjKznfChH9DQb
1561 sp6JS7f14BuwFY8MwjbC5GvngRCk6
1562 sp6JS7f14BuwFY8Mwk3Lb7FPe1629
1563 sp6JS7f14BuwFY8MwkCeS41BwVrBD
1564 sp6JS7f14BuwFY8MwkDnnvRyuWJ7d
1565 sp6JS7f14BuwFY8MwkbkRNnzDEFpf
1566 sp6JS7f14BuwFY8MwkiNhaVhGNk6v
1567 sp6JS7f14BuwFY8Mwm1X4UJXRZx3p
1568 sp6JS7f14BuwFY8Mwm7da9q5vfq7J
1569 sp6JS7f14BuwFY8MwmPLqfBPrHw5H
1570 sp6JS7f14BuwFY8MwmbJpxvVjEwm2
1571 sp6JS7f14BuwFY8MwoAVeA7ka37cD
1572 sp6JS7f14BuwFY8MwoTFFTAwFKmVM
1573 sp6JS7f14BuwFY8MwoYsne51VpDE3
1574 sp6JS7f14BuwFY8MwohLVnU1VTk5h
1575
1576#endif // 0
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
bool isArray() const
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
std::string toStyledString() const
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition suite.h:226
void testConsecutivePacking(FeatureBitset features)
void testNFTokenDir(FeatureBitset features)
void printNFTPages(test::jtx::Env &env, Volume vol)
void testWithFeats(FeatureBitset features)
void testLopsidedSplits(FeatureBitset features)
void testTooManyEquivalent(FeatureBitset features)
void run() override
Runs the suite.
void testConsecutiveNFTs(FeatureBitset features)
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:484
A transaction testing environment.
Definition Env.h:102
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:772
T clear(T... args)
T emplace_back(T... args)
T empty(T... args)
T end(T... args)
T endl(T... args)
T erase(T... args)
T find(T... args)
T front(T... args)
T insert(T... args)
@ arrayValue
array value (ordered list)
Definition json_value.h:25
unsigned int UInt
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Definition Indexes.cpp:408
Taxon cipheredTaxon(std::uint32_t tokenSeq, Taxon taxon)
Definition nft.h:65
Taxon toTaxon(std::uint32_t i)
Definition nft.h:23
uint256 constexpr pageMask(std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff"))
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
constexpr std::uint32_t const tfSellNFToken
Definition TxFlags.h:211
@ tecNO_SUITABLE_NFTOKEN_PAGE
Definition TER.h:303
@ tesSUCCESS
Definition TER.h:226
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
constexpr std::uint32_t const tfTransferable
Definition TxFlags.h:123
T pop_back(T... args)
T push_back(T... args)
T reserve(T... args)
T reverse(T... args)
T size(T... args)
uint256 key
Definition Keylet.h:21