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