148 using namespace test::jtx;
150 Env env{*
this, features};
158 AcctStat(
char const* name) : acct(name)
167 AcctStat alice{
"alice"};
168 AcctStat becky{
"becky"};
169 AcctStat minter{
"minter"};
171 env.fund(XRP(10000), alice, becky, minter);
175 env(token::setMinter(alice, minter));
196 alice.nfts.reserve(105);
197 while (alice.nfts.size() < 105)
200 alice.nfts.push_back(token::getNextID(
202 env(token::mint(alice),
204 token::xferFee(xferFee));
208 minter.nfts.reserve(105);
209 while (minter.nfts.size() < 105)
212 minter.nfts.push_back(token::getNextID(
214 env(token::mint(minter),
216 token::xferFee(xferFee),
217 token::issuer(alice));
223 becky.nfts.reserve(70);
225 auto aliceIter = alice.nfts.begin();
226 auto minterIter = minter.nfts.begin();
227 while (becky.nfts.size() < 70)
230 auto xferNFT = [&env, &becky](AcctStat& acct,
auto& iter) {
233 env(token::createOffer(acct, *iter, XRP(0)),
236 env(token::acceptSellOffer(becky, offerIndex));
238 becky.nfts.push_back(*iter);
239 iter = acct.nfts.erase(iter);
242 xferNFT(alice, aliceIter);
243 xferNFT(minter, minterIter);
245 BEAST_EXPECT(aliceIter == alice.nfts.end());
246 BEAST_EXPECT(minterIter == minter.nfts.end());
250 BEAST_EXPECT(
nftCount(env, alice.acct) == 70);
251 BEAST_EXPECT(
nftCount(env, becky.acct) == 70);
252 BEAST_EXPECT(
nftCount(env, minter.acct) == 70);
257 [&env](AcctStat& owner, AcctStat& other1, AcctStat& other2) {
261 env(token::createOffer(owner, nft, drops(1)),
263 token::destination(other1));
264 env(token::createOffer(owner, nft, drops(1)),
266 token::destination(other2));
270 env(token::createOffer(other1, nft, drops(1)),
271 token::owner(owner));
272 env(token::createOffer(other2, nft, drops(1)),
273 token::owner(owner));
276 env(token::createOffer(other2, nft, drops(2)),
277 token::owner(owner));
278 env(token::createOffer(other1, nft, drops(2)),
279 token::owner(owner));
283 addOffers(alice, becky, minter);
284 addOffers(becky, minter, alice);
285 addOffers(minter, alice, becky);
286 BEAST_EXPECT(ownerCount(env, alice) == 424);
287 BEAST_EXPECT(ownerCount(env, becky) == 424);
288 BEAST_EXPECT(ownerCount(env, minter) == 424);
293 AcctStat*
const stats[3] = {&alice, &becky, &minter};
297 while (stats[0]->nfts.size() > 0 || stats[1]->nfts.size() > 0 ||
298 stats[2]->nfts.size() > 0)
302 AcctStat& owner = *(stats[acctDist(engine)]);
303 if (owner.nfts.empty())
308 0lu, owner.nfts.size() - 1);
309 auto nftIter = owner.nfts.begin() + nftDist(engine);
311 owner.nfts.erase(nftIter);
316 AcctStat& burner = owner.acct == becky.acct
317 ? *(stats[acctDist(engine)])
318 : mintDist(engine) ? alice
321 if (owner.acct == burner.acct)
322 env(token::burn(burner, nft));
324 env(token::burn(burner, nft), token::owner(owner));
329 BEAST_EXPECT(
nftCount(env, alice.acct) == alice.nfts.size());
330 BEAST_EXPECT(
nftCount(env, becky.acct) == becky.nfts.size());
331 BEAST_EXPECT(
nftCount(env, minter.acct) == minter.nfts.size());
333 BEAST_EXPECT(
nftCount(env, alice.acct) == 0);
334 BEAST_EXPECT(
nftCount(env, becky.acct) == 0);
335 BEAST_EXPECT(
nftCount(env, minter.acct) == 0);
339 BEAST_EXPECT(ownerCount(env, alice) == 0);
340 BEAST_EXPECT(ownerCount(env, becky) == 0);
341 BEAST_EXPECT(ownerCount(env, minter) == 0);
353 using namespace test::jtx;
355 Account
const alice{
"alice"};
357 Env env{*
this, features};
358 env.fund(XRP(1000), alice);
362 auto genPackedTokens = [
this, &env, &alice]() {
374 auto internalTaxon = [&env](
378 env.le(acct)->at(~sfMintedNFTokens).value_or(0);
382 if (env.current()->rules().enabled(fixNFTokenRemint))
383 tokenSeq += env.le(acct)
384 ->at(~sfFirstNFTokenSequence)
385 .value_or(env.seq(acct));
399 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
400 uint32_t
const extTaxon = internalTaxon(alice, intTaxon);
401 nfts.
push_back(token::getNextID(env, alice, extTaxon));
402 env(token::mint(alice, extTaxon));
413 jvParams[jss::ledger_index] =
"current";
414 jvParams[jss::binary] =
false;
419 boost::lexical_cast<std::string>(jvParams));
426 if (state[i].isMember(sfNFTokens.jsonName) &&
427 state[i][sfNFTokens.jsonName].
isArray())
430 state[i][sfNFTokens.jsonName].
size() == 32);
436 BEAST_EXPECT(pageCount == 3);
445 BEAST_EXPECT(
nftCount(env, alice) == 96);
446 BEAST_EXPECT(ownerCount(env, alice) == 3);
448 for (
uint256 const& nft : nfts)
450 env(token::burn(alice, {nft}));
453 BEAST_EXPECT(
nftCount(env, alice) == 0);
454 BEAST_EXPECT(ownerCount(env, alice) == 0);
458 auto checkNoTokenPages = [
this, &env]() {
460 jvParams[jss::ledger_index] =
"current";
461 jvParams[jss::binary] =
false;
466 boost::lexical_cast<std::string>(jvParams));
472 BEAST_EXPECT(!state[i].isMember(sfNFTokens.jsonName));
482 BEAST_EXPECT(
nftCount(env, alice) == 96);
483 BEAST_EXPECT(ownerCount(env, alice) == 3);
488 if (!BEAST_EXPECT(lastNFTokenPage))
491 uint256 const middleNFTokenPageIndex =
492 lastNFTokenPage->at(sfPreviousPageMin);
495 if (!BEAST_EXPECT(middleNFTokenPage))
498 uint256 const firstNFTokenPageIndex =
499 middleNFTokenPage->at(sfPreviousPageMin);
502 if (!BEAST_EXPECT(firstNFTokenPage))
506 for (
int i = 0; i < 31; ++i)
508 env(token::burn(alice, {nfts.
back()}));
516 if (!BEAST_EXPECT(lastNFTokenPage))
520 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
521 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
522 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
525 env(token::burn(alice, {nfts.
back()}));
529 if (features[fixNFTokenPageLinks])
536 BEAST_EXPECT(lastNFTokenPage);
538 lastNFTokenPage->at(~sfPreviousPageMin) ==
539 firstNFTokenPageIndex);
540 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
542 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
547 BEAST_EXPECT(!middleNFTokenPage);
553 BEAST_EXPECT(firstNFTokenPage);
555 !firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
557 firstNFTokenPage->at(~sfNextPageMin) ==
558 lastNFTokenPage->key());
560 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
568 BEAST_EXPECT(!lastNFTokenPage);
574 if (!BEAST_EXPECT(middleNFTokenPage))
577 middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
578 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
582 while (!nfts.
empty())
584 env(token::burn(alice, {nfts.
back()}));
588 BEAST_EXPECT(
nftCount(env, alice) == 0);
589 BEAST_EXPECT(ownerCount(env, alice) == 0);
597 BEAST_EXPECT(
nftCount(env, alice) == 96);
598 BEAST_EXPECT(ownerCount(env, alice) == 3);
603 if (!BEAST_EXPECT(lastNFTokenPage))
606 uint256 const middleNFTokenPageIndex =
607 lastNFTokenPage->at(sfPreviousPageMin);
610 if (!BEAST_EXPECT(middleNFTokenPage))
613 uint256 const firstNFTokenPageIndex =
614 middleNFTokenPage->at(sfPreviousPageMin);
617 if (!BEAST_EXPECT(firstNFTokenPage))
622 env(token::burn(alice, nfts[i]));
626 BEAST_EXPECT(
nftCount(env, alice) == 64);
627 BEAST_EXPECT(ownerCount(env, alice) == 2);
633 BEAST_EXPECT(!middleNFTokenPage);
636 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
638 lastNFTokenPage->getFieldH256(sfPreviousPageMin) ==
639 firstNFTokenPageIndex);
644 firstNFTokenPage->getFieldH256(sfNextPageMin) ==
646 BEAST_EXPECT(!firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
649 for (
uint256 const& nft : nfts)
651 env(token::burn(alice, {nft}));
654 BEAST_EXPECT(
nftCount(env, alice) == 0);
655 BEAST_EXPECT(ownerCount(env, alice) == 0);
663 BEAST_EXPECT(
nftCount(env, alice) == 96);
664 BEAST_EXPECT(ownerCount(env, alice) == 3);
669 if (!BEAST_EXPECT(lastNFTokenPage))
672 uint256 const middleNFTokenPageIndex =
673 lastNFTokenPage->at(sfPreviousPageMin);
676 if (!BEAST_EXPECT(middleNFTokenPage))
679 uint256 const firstNFTokenPageIndex =
680 middleNFTokenPage->at(sfPreviousPageMin);
683 if (!BEAST_EXPECT(firstNFTokenPage))
688 for (
int i = 0; i < 32; ++i)
690 env(token::burn(alice, {nfts.
back()}));
698 BEAST_EXPECT(!firstNFTokenPage);
703 if (!BEAST_EXPECT(middleNFTokenPage))
705 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
706 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfNextPageMin));
709 if (!BEAST_EXPECT(lastNFTokenPage))
711 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
712 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
716 for (
int i = 0; i < 32; ++i)
718 env(token::burn(alice, {nfts.
back()}));
723 if (features[fixNFTokenPageLinks])
730 BEAST_EXPECT(lastNFTokenPage);
732 !lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
733 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
735 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
740 BEAST_EXPECT(!middleNFTokenPage);
745 BEAST_EXPECT(!firstNFTokenPage);
753 BEAST_EXPECT(!lastNFTokenPage);
759 if (!BEAST_EXPECT(middleNFTokenPage))
762 !middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
763 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
767 while (!nfts.
empty())
769 env(token::burn(alice, {nfts.
back()}));
773 BEAST_EXPECT(
nftCount(env, alice) == 0);
774 BEAST_EXPECT(ownerCount(env, alice) == 0);
778 if (features[fixNFTokenPageLinks])
792 BEAST_EXPECT(
nftCount(env, alice) == 96);
793 BEAST_EXPECT(ownerCount(env, alice) == 3);
796 for (
int i = 0; i < 31; ++i)
798 env(token::burn(alice, {nfts.
back()}));
814 env.current()->fees().base,
819 auto lastNFTokenPage =
821 if (!BEAST_EXPECT(lastNFTokenPage))
824 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
827 ac.view().erase(lastNFTokenPage);
831 for (
TER const& terExpect :
834 terActual = ac.checkInvariants(terActual,
XRPAmount{});
835 BEAST_EXPECT(terExpect == terActual);
837 sink.messages().str().starts_with(
"Invariant failed:"));
841 sink.messages().str().find(
842 "Last NFT page deleted with non-empty directory") !=
858 env.current()->fees().base,
863 auto lastNFTokenPage =
867 lastNFTokenPage->getFieldH256(sfPreviousPageMin)));
868 BEAST_EXPECT(middleNFTokenPage);
872 middleNFTokenPage->makeFieldAbsent(sfNextPageMin);
873 ac.view().update(middleNFTokenPage);
877 for (
TER const& terExpect :
880 terActual = ac.checkInvariants(terActual,
XRPAmount{});
881 BEAST_EXPECT(terExpect == terActual);
883 sink.messages().str().starts_with(
"Invariant failed:"));
887 sink.messages().str().find(
"Lost NextMinPage link") !=
900 using namespace test::jtx;
904 if (!features[fixNonFungibleTokensV1_2])
906 Env env{*
this, features};
908 Account
const alice(
"alice");
909 Account
const becky(
"becky");
910 env.fund(XRP(1000), alice, becky);
926 env(token::mint(alice, 0),
936 env.fund(XRP(1000), acct);
941 env(token::createOffer(acct, nftokenID, drops(1)),
942 token::owner(alice));
947 for (
uint256 const& offerIndex : offerIndexes)
953 uint256 const beckyOfferIndex =
955 env(token::createOffer(becky, nftokenID, drops(1)),
956 token::owner(alice));
959 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
963 for (
int i = 0; i < 10; ++i)
968 env(token::cancelOffer(becky, {beckyOfferIndex}));
971 uint256 const aliceOfferIndex =
973 env(token::createOffer(alice, nftokenID, drops(1)),
977 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
981 env(token::cancelOffer(alice, {aliceOfferIndex}));
984 env(token::burn(alice, nftokenID));
988 for (
uint256 const& offerIndex : offerIndexes)
994 BEAST_EXPECT(ownerCount(env, alice) == 0);
995 BEAST_EXPECT(ownerCount(env, becky) == 0);
1002 if (features[fixNonFungibleTokensV1_2])
1004 Env env{*
this, features};
1006 Account
const alice(
"alice");
1007 Account
const becky(
"becky");
1008 env.fund(XRP(100000), alice, becky);
1019 for (
uint256 const& offerIndex : offerIndexes)
1025 uint256 const beckyOfferIndex =
1027 env(token::createOffer(becky, nftokenID, drops(1)),
1028 token::owner(alice));
1032 env(token::burn(alice, nftokenID));
1037 for (
uint256 const& offerIndex : offerIndexes)
1047 BEAST_EXPECT(ownerCount(env, alice) == 0);
1048 BEAST_EXPECT(ownerCount(env, becky) == 0);
1053 if (features[fixNonFungibleTokensV1_2])
1055 Env env{*
this, features};
1057 Account
const alice(
"alice");
1058 Account
const becky(
"becky");
1059 env.fund(XRP(100000), alice, becky);
1070 for (
uint256 const& offerIndex : offerIndexes)
1076 env(token::burn(alice, nftokenID));
1079 uint32_t offerDeletedCount = 0;
1081 for (
uint256 const& offerIndex : offerIndexes)
1084 offerDeletedCount++;
1093 BEAST_EXPECT(ownerCount(env, alice) == 1);
1098 if (features[fixNonFungibleTokensV1_2])
1100 Env env{*
this, features};
1102 Account
const alice(
"alice");
1103 Account
const becky(
"becky");
1104 env.fund(XRP(100000), alice, becky);
1116 for (
uint256 const& offerIndex : offerIndexes)
1122 env(token::createOffer(becky, nftokenID, drops(1)),
1123 token::owner(alice));
1125 env(token::createOffer(becky, nftokenID, drops(1)),
1126 token::owner(alice));
1130 env(token::burn(alice, nftokenID));
1135 for (
uint256 const& offerIndex : offerIndexes)
1142 BEAST_EXPECT(ownerCount(env, alice) == 0);
1145 BEAST_EXPECT(ownerCount(env, becky) == 1);
1154 if (features[fixNFTokenPageLinks])
1162 using namespace test::jtx;
1164 Account
const alice{
"alice"};
1165 Account
const minter{
"minter"};
1167 Env env{*
this, features};
1168 env.fund(XRP(1000), alice, minter);
1172 auto genPackedTokens = [
this, &env, &alice, &minter]() {
1184 auto internalTaxon = [&env](
1185 Account
const& acct,
1188 env.le(acct)->at(~sfMintedNFTokens).value_or(0);
1192 if (env.current()->rules().enabled(fixNFTokenRemint))
1193 tokenSeq += env.le(acct)
1194 ->at(~sfFirstNFTokenSequence)
1195 .value_or(env.seq(acct));
1209 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
1210 uint32_t
const extTaxon = internalTaxon(minter, intTaxon);
1217 uint256 const minterOfferIndex =
1219 env(token::createOffer(minter, nfts.
back(), XRP(0)),
1224 env(token::acceptSellOffer(alice, minterOfferIndex));
1235 jvParams[jss::ledger_index] =
"current";
1236 jvParams[jss::binary] =
false;
1241 boost::lexical_cast<std::string>(jvParams));
1243 Json::Value& state = jrr[jss::result][jss::state];
1248 if (state[i].isMember(sfNFTokens.jsonName) &&
1249 state[i][sfNFTokens.jsonName].
isArray())
1252 state[i][sfNFTokens.jsonName].
size() == 32);
1258 BEAST_EXPECT(pageCount == 3);
1265 BEAST_EXPECT(
nftCount(env, alice) == 96);
1266 BEAST_EXPECT(ownerCount(env, alice) == 3);
1271 if (!BEAST_EXPECT(lastNFTokenPage))
1274 uint256 const middleNFTokenPageIndex =
1275 lastNFTokenPage->at(sfPreviousPageMin);
1278 if (!BEAST_EXPECT(middleNFTokenPage))
1281 uint256 const firstNFTokenPageIndex =
1282 middleNFTokenPage->at(sfPreviousPageMin);
1283 auto firstNFTokenPage = env.le(
1285 if (!BEAST_EXPECT(firstNFTokenPage))
1290 for (
int i = 0; i < 32; ++i)
1296 uint256 const aliceOfferIndex =
1298 env(token::createOffer(alice, last32NFTs.
back(), XRP(0)),
1303 env(token::acceptSellOffer(minter, aliceOfferIndex));
1311 BEAST_EXPECT(!lastNFTokenPage);
1312 BEAST_EXPECT(ownerCount(env, alice) == 2);
1318 if (!BEAST_EXPECT(middleNFTokenPage))
1320 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
1321 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
1324 auto const acctDelFee{drops(env.current()->fees().increment)};
1325 env(acctdelete(alice, minter),
1331 for (
uint256 nftID : last32NFTs)
1334 uint256 const minterOfferIndex =
1336 env(token::createOffer(minter, nftID, XRP(0)),
1341 env(token::acceptSellOffer(alice, minterOfferIndex));
1344 BEAST_EXPECT(ownerCount(env, alice) == 3);
1352 params[jss::account] = alice.human();
1353 return env.rpc(
"json",
"account_objects",
to_string(params));
1355 BEAST_EXPECT(!acctObjs.
isMember(jss::marker));
1357 acctObjs[jss::result][jss::account_objects].size() == 2);
1364 params[jss::account] = alice.human();
1365 params[jss::type] =
"state";
1366 return env.rpc(
"json",
"account_nfts",
to_string(params));
1368 BEAST_EXPECT(!aliceNFTs.
isMember(jss::marker));
1370 aliceNFTs[jss::result][jss::account_nfts].size() == 64);