146 using namespace test::jtx;
148 Env env{*
this, features};
156 AcctStat(
char const* name) : acct(name)
165 AcctStat alice{
"alice"};
166 AcctStat becky{
"becky"};
167 AcctStat minter{
"minter"};
169 env.fund(XRP(10000), alice, becky, minter);
173 env(token::setMinter(alice, minter));
194 alice.nfts.reserve(105);
195 while (alice.nfts.size() < 105)
198 alice.nfts.push_back(token::getNextID(
200 env(token::mint(alice),
202 token::xferFee(xferFee));
206 minter.nfts.reserve(105);
207 while (minter.nfts.size() < 105)
210 minter.nfts.push_back(token::getNextID(
212 env(token::mint(minter),
214 token::xferFee(xferFee),
215 token::issuer(alice));
221 becky.nfts.reserve(70);
223 auto aliceIter = alice.nfts.begin();
224 auto minterIter = minter.nfts.begin();
225 while (becky.nfts.size() < 70)
228 auto xferNFT = [&env, &becky](AcctStat& acct,
auto& iter) {
231 env(token::createOffer(acct, *iter, XRP(0)),
234 env(token::acceptSellOffer(becky, offerIndex));
236 becky.nfts.push_back(*iter);
237 iter = acct.nfts.erase(iter);
240 xferNFT(alice, aliceIter);
241 xferNFT(minter, minterIter);
243 BEAST_EXPECT(aliceIter == alice.nfts.end());
244 BEAST_EXPECT(minterIter == minter.nfts.end());
248 BEAST_EXPECT(
nftCount(env, alice.acct) == 70);
249 BEAST_EXPECT(
nftCount(env, becky.acct) == 70);
250 BEAST_EXPECT(
nftCount(env, minter.acct) == 70);
255 [&env](AcctStat& owner, AcctStat& other1, AcctStat& other2) {
259 env(token::createOffer(owner, nft, drops(1)),
261 token::destination(other1));
262 env(token::createOffer(owner, nft, drops(1)),
264 token::destination(other2));
268 env(token::createOffer(other1, nft, drops(1)),
269 token::owner(owner));
270 env(token::createOffer(other2, nft, drops(1)),
271 token::owner(owner));
274 env(token::createOffer(other2, nft, drops(2)),
275 token::owner(owner));
276 env(token::createOffer(other1, nft, drops(2)),
277 token::owner(owner));
281 addOffers(alice, becky, minter);
282 addOffers(becky, minter, alice);
283 addOffers(minter, alice, becky);
284 BEAST_EXPECT(ownerCount(env, alice) == 424);
285 BEAST_EXPECT(ownerCount(env, becky) == 424);
286 BEAST_EXPECT(ownerCount(env, minter) == 424);
291 AcctStat*
const stats[3] = {&alice, &becky, &minter};
295 while (stats[0]->nfts.size() > 0 || stats[1]->nfts.size() > 0 ||
296 stats[2]->nfts.size() > 0)
300 AcctStat& owner = *(stats[acctDist(engine)]);
301 if (owner.nfts.empty())
306 0lu, owner.nfts.size() - 1);
307 auto nftIter = owner.nfts.begin() + nftDist(engine);
309 owner.nfts.erase(nftIter);
314 AcctStat& burner = owner.acct == becky.acct
315 ? *(stats[acctDist(engine)])
316 : mintDist(engine) ? alice
319 if (owner.acct == burner.acct)
320 env(token::burn(burner, nft));
322 env(token::burn(burner, nft), token::owner(owner));
327 BEAST_EXPECT(
nftCount(env, alice.acct) == alice.nfts.size());
328 BEAST_EXPECT(
nftCount(env, becky.acct) == becky.nfts.size());
329 BEAST_EXPECT(
nftCount(env, minter.acct) == minter.nfts.size());
331 BEAST_EXPECT(
nftCount(env, alice.acct) == 0);
332 BEAST_EXPECT(
nftCount(env, becky.acct) == 0);
333 BEAST_EXPECT(
nftCount(env, minter.acct) == 0);
337 BEAST_EXPECT(ownerCount(env, alice) == 0);
338 BEAST_EXPECT(ownerCount(env, becky) == 0);
339 BEAST_EXPECT(ownerCount(env, minter) == 0);
351 using namespace test::jtx;
353 Account
const alice{
"alice"};
355 Env env{*
this, features};
356 env.fund(XRP(1000), alice);
360 auto genPackedTokens = [
this, &env, &alice]() {
372 auto internalTaxon = [&env](
376 env.le(acct)->at(~sfMintedNFTokens).value_or(0);
380 if (env.current()->rules().enabled(fixNFTokenRemint))
381 tokenSeq += env.le(acct)
382 ->at(~sfFirstNFTokenSequence)
383 .value_or(env.seq(acct));
397 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
398 uint32_t
const extTaxon = internalTaxon(alice, intTaxon);
399 nfts.
push_back(token::getNextID(env, alice, extTaxon));
400 env(token::mint(alice, extTaxon));
411 jvParams[jss::ledger_index] =
"current";
412 jvParams[jss::binary] =
false;
415 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
422 if (state[i].isMember(sfNFTokens.jsonName) &&
423 state[i][sfNFTokens.jsonName].
isArray())
426 state[i][sfNFTokens.jsonName].
size() == 32);
432 BEAST_EXPECT(pageCount == 3);
441 BEAST_EXPECT(
nftCount(env, alice) == 96);
442 BEAST_EXPECT(ownerCount(env, alice) == 3);
444 for (
uint256 const& nft : nfts)
446 env(token::burn(alice, {nft}));
449 BEAST_EXPECT(
nftCount(env, alice) == 0);
450 BEAST_EXPECT(ownerCount(env, alice) == 0);
454 auto checkNoTokenPages = [
this, &env]() {
456 jvParams[jss::ledger_index] =
"current";
457 jvParams[jss::binary] =
false;
460 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
466 BEAST_EXPECT(!state[i].isMember(sfNFTokens.jsonName));
476 BEAST_EXPECT(
nftCount(env, alice) == 96);
477 BEAST_EXPECT(ownerCount(env, alice) == 3);
482 if (!BEAST_EXPECT(lastNFTokenPage))
485 uint256 const middleNFTokenPageIndex =
486 lastNFTokenPage->at(sfPreviousPageMin);
489 if (!BEAST_EXPECT(middleNFTokenPage))
492 uint256 const firstNFTokenPageIndex =
493 middleNFTokenPage->at(sfPreviousPageMin);
496 if (!BEAST_EXPECT(firstNFTokenPage))
500 for (
int i = 0; i < 31; ++i)
502 env(token::burn(alice, {nfts.
back()}));
510 if (!BEAST_EXPECT(lastNFTokenPage))
514 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
515 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
516 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
519 env(token::burn(alice, {nfts.
back()}));
523 if (features[fixNFTokenPageLinks])
530 BEAST_EXPECT(lastNFTokenPage);
532 lastNFTokenPage->at(~sfPreviousPageMin) ==
533 firstNFTokenPageIndex);
534 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
536 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
541 BEAST_EXPECT(!middleNFTokenPage);
547 BEAST_EXPECT(firstNFTokenPage);
549 !firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
551 firstNFTokenPage->at(~sfNextPageMin) ==
552 lastNFTokenPage->key());
554 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
562 BEAST_EXPECT(!lastNFTokenPage);
568 if (!BEAST_EXPECT(middleNFTokenPage))
571 middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
572 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
576 while (!nfts.
empty())
578 env(token::burn(alice, {nfts.
back()}));
582 BEAST_EXPECT(
nftCount(env, alice) == 0);
583 BEAST_EXPECT(ownerCount(env, alice) == 0);
591 BEAST_EXPECT(
nftCount(env, alice) == 96);
592 BEAST_EXPECT(ownerCount(env, alice) == 3);
597 if (!BEAST_EXPECT(lastNFTokenPage))
600 uint256 const middleNFTokenPageIndex =
601 lastNFTokenPage->at(sfPreviousPageMin);
604 if (!BEAST_EXPECT(middleNFTokenPage))
607 uint256 const firstNFTokenPageIndex =
608 middleNFTokenPage->at(sfPreviousPageMin);
611 if (!BEAST_EXPECT(firstNFTokenPage))
616 env(token::burn(alice, nfts[i]));
620 BEAST_EXPECT(
nftCount(env, alice) == 64);
621 BEAST_EXPECT(ownerCount(env, alice) == 2);
627 BEAST_EXPECT(!middleNFTokenPage);
630 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
632 lastNFTokenPage->getFieldH256(sfPreviousPageMin) ==
633 firstNFTokenPageIndex);
638 firstNFTokenPage->getFieldH256(sfNextPageMin) ==
640 BEAST_EXPECT(!firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
643 for (
uint256 const& nft : nfts)
645 env(token::burn(alice, {nft}));
648 BEAST_EXPECT(
nftCount(env, alice) == 0);
649 BEAST_EXPECT(ownerCount(env, alice) == 0);
657 BEAST_EXPECT(
nftCount(env, alice) == 96);
658 BEAST_EXPECT(ownerCount(env, alice) == 3);
663 if (!BEAST_EXPECT(lastNFTokenPage))
666 uint256 const middleNFTokenPageIndex =
667 lastNFTokenPage->at(sfPreviousPageMin);
670 if (!BEAST_EXPECT(middleNFTokenPage))
673 uint256 const firstNFTokenPageIndex =
674 middleNFTokenPage->at(sfPreviousPageMin);
677 if (!BEAST_EXPECT(firstNFTokenPage))
682 for (
int i = 0; i < 32; ++i)
684 env(token::burn(alice, {nfts.
back()}));
692 BEAST_EXPECT(!firstNFTokenPage);
697 if (!BEAST_EXPECT(middleNFTokenPage))
699 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
700 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfNextPageMin));
703 if (!BEAST_EXPECT(lastNFTokenPage))
705 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
706 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
710 for (
int i = 0; i < 32; ++i)
712 env(token::burn(alice, {nfts.
back()}));
717 if (features[fixNFTokenPageLinks])
724 BEAST_EXPECT(lastNFTokenPage);
726 !lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
727 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
729 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
734 BEAST_EXPECT(!middleNFTokenPage);
739 BEAST_EXPECT(!firstNFTokenPage);
747 BEAST_EXPECT(!lastNFTokenPage);
753 if (!BEAST_EXPECT(middleNFTokenPage))
756 !middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
757 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
761 while (!nfts.
empty())
763 env(token::burn(alice, {nfts.
back()}));
767 BEAST_EXPECT(
nftCount(env, alice) == 0);
768 BEAST_EXPECT(ownerCount(env, alice) == 0);
772 if (features[fixNFTokenPageLinks])
786 BEAST_EXPECT(
nftCount(env, alice) == 96);
787 BEAST_EXPECT(ownerCount(env, alice) == 3);
790 for (
int i = 0; i < 31; ++i)
792 env(token::burn(alice, {nfts.
back()}));
808 env.current()->fees().base,
813 auto lastNFTokenPage =
815 if (!BEAST_EXPECT(lastNFTokenPage))
818 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
821 ac.view().erase(lastNFTokenPage);
825 for (
TER const& terExpect :
828 terActual = ac.checkInvariants(terActual,
XRPAmount{});
829 BEAST_EXPECT(terExpect == terActual);
831 sink.messages().str().starts_with(
"Invariant failed:"));
835 sink.messages().str().find(
836 "Last NFT page deleted with non-empty directory") !=
852 env.current()->fees().base,
857 auto lastNFTokenPage =
861 lastNFTokenPage->getFieldH256(sfPreviousPageMin)));
862 BEAST_EXPECT(middleNFTokenPage);
866 middleNFTokenPage->makeFieldAbsent(sfNextPageMin);
867 ac.view().update(middleNFTokenPage);
871 for (
TER const& terExpect :
874 terActual = ac.checkInvariants(terActual,
XRPAmount{});
875 BEAST_EXPECT(terExpect == terActual);
877 sink.messages().str().starts_with(
"Invariant failed:"));
881 sink.messages().str().find(
"Lost NextMinPage link") !=
894 using namespace test::jtx;
898 if (!features[fixNonFungibleTokensV1_2])
900 Env env{*
this, features};
902 Account
const alice(
"alice");
903 Account
const becky(
"becky");
904 env.fund(XRP(1000), alice, becky);
920 env(token::mint(alice, 0),
930 env.fund(XRP(1000), acct);
935 env(token::createOffer(acct, nftokenID, drops(1)),
936 token::owner(alice));
941 for (
uint256 const& offerIndex : offerIndexes)
947 uint256 const beckyOfferIndex =
949 env(token::createOffer(becky, nftokenID, drops(1)),
950 token::owner(alice));
953 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
957 for (
int i = 0; i < 10; ++i)
962 env(token::cancelOffer(becky, {beckyOfferIndex}));
965 uint256 const aliceOfferIndex =
967 env(token::createOffer(alice, nftokenID, drops(1)),
971 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
975 env(token::cancelOffer(alice, {aliceOfferIndex}));
978 env(token::burn(alice, nftokenID));
982 for (
uint256 const& offerIndex : offerIndexes)
988 BEAST_EXPECT(ownerCount(env, alice) == 0);
989 BEAST_EXPECT(ownerCount(env, becky) == 0);
996 if (features[fixNonFungibleTokensV1_2])
998 Env env{*
this, features};
1000 Account
const alice(
"alice");
1001 Account
const becky(
"becky");
1002 env.fund(XRP(100000), alice, becky);
1013 for (
uint256 const& offerIndex : offerIndexes)
1019 uint256 const beckyOfferIndex =
1021 env(token::createOffer(becky, nftokenID, drops(1)),
1022 token::owner(alice));
1026 env(token::burn(alice, nftokenID));
1031 for (
uint256 const& offerIndex : offerIndexes)
1041 BEAST_EXPECT(ownerCount(env, alice) == 0);
1042 BEAST_EXPECT(ownerCount(env, becky) == 0);
1047 if (features[fixNonFungibleTokensV1_2])
1049 Env env{*
this, features};
1051 Account
const alice(
"alice");
1052 Account
const becky(
"becky");
1053 env.fund(XRP(100000), alice, becky);
1064 for (
uint256 const& offerIndex : offerIndexes)
1070 env(token::burn(alice, nftokenID));
1073 uint32_t offerDeletedCount = 0;
1075 for (
uint256 const& offerIndex : offerIndexes)
1078 offerDeletedCount++;
1087 BEAST_EXPECT(ownerCount(env, alice) == 1);
1092 if (features[fixNonFungibleTokensV1_2])
1094 Env env{*
this, features};
1096 Account
const alice(
"alice");
1097 Account
const becky(
"becky");
1098 env.fund(XRP(100000), alice, becky);
1110 for (
uint256 const& offerIndex : offerIndexes)
1116 env(token::createOffer(becky, nftokenID, drops(1)),
1117 token::owner(alice));
1119 env(token::createOffer(becky, nftokenID, drops(1)),
1120 token::owner(alice));
1124 env(token::burn(alice, nftokenID));
1129 for (
uint256 const& offerIndex : offerIndexes)
1136 BEAST_EXPECT(ownerCount(env, alice) == 0);
1139 BEAST_EXPECT(ownerCount(env, becky) == 1);
1148 if (features[fixNFTokenPageLinks])
1156 using namespace test::jtx;
1158 Account
const alice{
"alice"};
1159 Account
const minter{
"minter"};
1161 Env env{*
this, features};
1162 env.fund(XRP(1000), alice, minter);
1166 auto genPackedTokens = [
this, &env, &alice, &minter]() {
1178 auto internalTaxon = [&env](
1179 Account
const& acct,
1182 env.le(acct)->at(~sfMintedNFTokens).value_or(0);
1186 if (env.current()->rules().enabled(fixNFTokenRemint))
1187 tokenSeq += env.le(acct)
1188 ->at(~sfFirstNFTokenSequence)
1189 .value_or(env.seq(acct));
1203 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
1204 uint32_t
const extTaxon = internalTaxon(minter, intTaxon);
1211 uint256 const minterOfferIndex =
1213 env(token::createOffer(minter, nfts.
back(), XRP(0)),
1218 env(token::acceptSellOffer(alice, minterOfferIndex));
1229 jvParams[jss::ledger_index] =
"current";
1230 jvParams[jss::binary] =
false;
1233 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
1235 Json::Value& state = jrr[jss::result][jss::state];
1240 if (state[i].isMember(sfNFTokens.jsonName) &&
1241 state[i][sfNFTokens.jsonName].
isArray())
1244 state[i][sfNFTokens.jsonName].
size() == 32);
1250 BEAST_EXPECT(pageCount == 3);
1257 BEAST_EXPECT(
nftCount(env, alice) == 96);
1258 BEAST_EXPECT(ownerCount(env, alice) == 3);
1263 if (!BEAST_EXPECT(lastNFTokenPage))
1266 uint256 const middleNFTokenPageIndex =
1267 lastNFTokenPage->at(sfPreviousPageMin);
1270 if (!BEAST_EXPECT(middleNFTokenPage))
1273 uint256 const firstNFTokenPageIndex =
1274 middleNFTokenPage->at(sfPreviousPageMin);
1275 auto firstNFTokenPage = env.le(
1277 if (!BEAST_EXPECT(firstNFTokenPage))
1282 for (
int i = 0; i < 32; ++i)
1288 uint256 const aliceOfferIndex =
1290 env(token::createOffer(alice, last32NFTs.
back(), XRP(0)),
1295 env(token::acceptSellOffer(minter, aliceOfferIndex));
1303 BEAST_EXPECT(!lastNFTokenPage);
1304 BEAST_EXPECT(ownerCount(env, alice) == 2);
1310 if (!BEAST_EXPECT(middleNFTokenPage))
1312 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
1313 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
1316 auto const acctDelFee{drops(env.current()->fees().increment)};
1317 env(acctdelete(alice, minter),
1323 for (
uint256 nftID : last32NFTs)
1326 uint256 const minterOfferIndex =
1328 env(token::createOffer(minter, nftID, XRP(0)),
1333 env(token::acceptSellOffer(alice, minterOfferIndex));
1336 BEAST_EXPECT(ownerCount(env, alice) == 3);
1344 params[jss::account] = alice.human();
1345 return env.rpc(
"json",
"account_objects",
to_string(params));
1347 BEAST_EXPECT(!acctObjs.
isMember(jss::marker));
1349 acctObjs[jss::result][jss::account_objects].size() == 2);
1356 params[jss::account] = alice.human();
1357 params[jss::type] =
"state";
1358 return env.rpc(
"json",
"account_nfts",
to_string(params));
1360 BEAST_EXPECT(!aliceNFTs.
isMember(jss::marker));
1362 aliceNFTs[jss::result][jss::account_nfts].size() == 64);