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);
379 tokenSeq += env.le(acct)
380 ->at(~sfFirstNFTokenSequence)
381 .value_or(env.seq(acct));
395 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
396 uint32_t
const extTaxon = internalTaxon(alice, intTaxon);
397 nfts.
push_back(token::getNextID(env, alice, extTaxon));
398 env(token::mint(alice, extTaxon));
409 jvParams[jss::ledger_index] =
"current";
410 jvParams[jss::binary] =
false;
413 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
420 if (state[i].isMember(sfNFTokens.jsonName) &&
421 state[i][sfNFTokens.jsonName].
isArray())
424 state[i][sfNFTokens.jsonName].
size() == 32);
430 BEAST_EXPECT(pageCount == 3);
439 BEAST_EXPECT(
nftCount(env, alice) == 96);
440 BEAST_EXPECT(ownerCount(env, alice) == 3);
442 for (
uint256 const& nft : nfts)
444 env(token::burn(alice, {nft}));
447 BEAST_EXPECT(
nftCount(env, alice) == 0);
448 BEAST_EXPECT(ownerCount(env, alice) == 0);
452 auto checkNoTokenPages = [
this, &env]() {
454 jvParams[jss::ledger_index] =
"current";
455 jvParams[jss::binary] =
false;
458 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
464 BEAST_EXPECT(!state[i].isMember(sfNFTokens.jsonName));
474 BEAST_EXPECT(
nftCount(env, alice) == 96);
475 BEAST_EXPECT(ownerCount(env, alice) == 3);
480 if (!BEAST_EXPECT(lastNFTokenPage))
483 uint256 const middleNFTokenPageIndex =
484 lastNFTokenPage->at(sfPreviousPageMin);
487 if (!BEAST_EXPECT(middleNFTokenPage))
490 uint256 const firstNFTokenPageIndex =
491 middleNFTokenPage->at(sfPreviousPageMin);
494 if (!BEAST_EXPECT(firstNFTokenPage))
498 for (
int i = 0; i < 31; ++i)
500 env(token::burn(alice, {nfts.
back()}));
508 if (!BEAST_EXPECT(lastNFTokenPage))
512 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
513 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
514 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
517 env(token::burn(alice, {nfts.
back()}));
521 if (features[fixNFTokenPageLinks])
528 BEAST_EXPECT(lastNFTokenPage);
530 lastNFTokenPage->at(~sfPreviousPageMin) ==
531 firstNFTokenPageIndex);
532 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
534 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
539 BEAST_EXPECT(!middleNFTokenPage);
545 BEAST_EXPECT(firstNFTokenPage);
547 !firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
549 firstNFTokenPage->at(~sfNextPageMin) ==
550 lastNFTokenPage->key());
552 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
560 BEAST_EXPECT(!lastNFTokenPage);
566 if (!BEAST_EXPECT(middleNFTokenPage))
569 middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
570 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
574 while (!nfts.
empty())
576 env(token::burn(alice, {nfts.
back()}));
580 BEAST_EXPECT(
nftCount(env, alice) == 0);
581 BEAST_EXPECT(ownerCount(env, alice) == 0);
589 BEAST_EXPECT(
nftCount(env, alice) == 96);
590 BEAST_EXPECT(ownerCount(env, alice) == 3);
595 if (!BEAST_EXPECT(lastNFTokenPage))
598 uint256 const middleNFTokenPageIndex =
599 lastNFTokenPage->at(sfPreviousPageMin);
602 if (!BEAST_EXPECT(middleNFTokenPage))
605 uint256 const firstNFTokenPageIndex =
606 middleNFTokenPage->at(sfPreviousPageMin);
609 if (!BEAST_EXPECT(firstNFTokenPage))
614 env(token::burn(alice, nfts[i]));
618 BEAST_EXPECT(
nftCount(env, alice) == 64);
619 BEAST_EXPECT(ownerCount(env, alice) == 2);
625 BEAST_EXPECT(!middleNFTokenPage);
628 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
630 lastNFTokenPage->getFieldH256(sfPreviousPageMin) ==
631 firstNFTokenPageIndex);
636 firstNFTokenPage->getFieldH256(sfNextPageMin) ==
638 BEAST_EXPECT(!firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
641 for (
uint256 const& nft : nfts)
643 env(token::burn(alice, {nft}));
646 BEAST_EXPECT(
nftCount(env, alice) == 0);
647 BEAST_EXPECT(ownerCount(env, alice) == 0);
655 BEAST_EXPECT(
nftCount(env, alice) == 96);
656 BEAST_EXPECT(ownerCount(env, alice) == 3);
661 if (!BEAST_EXPECT(lastNFTokenPage))
664 uint256 const middleNFTokenPageIndex =
665 lastNFTokenPage->at(sfPreviousPageMin);
668 if (!BEAST_EXPECT(middleNFTokenPage))
671 uint256 const firstNFTokenPageIndex =
672 middleNFTokenPage->at(sfPreviousPageMin);
675 if (!BEAST_EXPECT(firstNFTokenPage))
680 for (
int i = 0; i < 32; ++i)
682 env(token::burn(alice, {nfts.
back()}));
690 BEAST_EXPECT(!firstNFTokenPage);
695 if (!BEAST_EXPECT(middleNFTokenPage))
697 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
698 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfNextPageMin));
701 if (!BEAST_EXPECT(lastNFTokenPage))
703 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
704 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
708 for (
int i = 0; i < 32; ++i)
710 env(token::burn(alice, {nfts.
back()}));
715 if (features[fixNFTokenPageLinks])
722 BEAST_EXPECT(lastNFTokenPage);
724 !lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
725 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
727 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
732 BEAST_EXPECT(!middleNFTokenPage);
737 BEAST_EXPECT(!firstNFTokenPage);
745 BEAST_EXPECT(!lastNFTokenPage);
751 if (!BEAST_EXPECT(middleNFTokenPage))
754 !middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
755 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
759 while (!nfts.
empty())
761 env(token::burn(alice, {nfts.
back()}));
765 BEAST_EXPECT(
nftCount(env, alice) == 0);
766 BEAST_EXPECT(ownerCount(env, alice) == 0);
770 if (features[fixNFTokenPageLinks])
784 BEAST_EXPECT(
nftCount(env, alice) == 96);
785 BEAST_EXPECT(ownerCount(env, alice) == 3);
788 for (
int i = 0; i < 31; ++i)
790 env(token::burn(alice, {nfts.
back()}));
806 env.current()->fees().base,
811 auto lastNFTokenPage =
813 if (!BEAST_EXPECT(lastNFTokenPage))
816 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
819 ac.view().erase(lastNFTokenPage);
823 for (
TER const& terExpect :
826 terActual = ac.checkInvariants(terActual,
XRPAmount{});
827 BEAST_EXPECT(terExpect == terActual);
829 sink.messages().str().starts_with(
"Invariant failed:"));
833 sink.messages().str().find(
834 "Last NFT page deleted with non-empty directory") !=
850 env.current()->fees().base,
855 auto lastNFTokenPage =
859 lastNFTokenPage->getFieldH256(sfPreviousPageMin)));
860 BEAST_EXPECT(middleNFTokenPage);
864 middleNFTokenPage->makeFieldAbsent(sfNextPageMin);
865 ac.view().update(middleNFTokenPage);
869 for (
TER const& terExpect :
872 terActual = ac.checkInvariants(terActual,
XRPAmount{});
873 BEAST_EXPECT(terExpect == terActual);
875 sink.messages().str().starts_with(
"Invariant failed:"));
879 sink.messages().str().find(
"Lost NextMinPage link") !=
892 using namespace test::jtx;
898 Env env{*
this, features};
900 Account
const alice(
"alice");
901 Account
const becky(
"becky");
902 env.fund(XRP(100000), alice, becky);
913 for (
uint256 const& offerIndex : offerIndexes)
919 uint256 const beckyOfferIndex =
921 env(token::createOffer(becky, nftokenID, drops(1)),
922 token::owner(alice));
926 env(token::burn(alice, nftokenID));
931 for (
uint256 const& offerIndex : offerIndexes)
941 BEAST_EXPECT(ownerCount(env, alice) == 0);
942 BEAST_EXPECT(ownerCount(env, becky) == 0);
947 Env env{*
this, features};
949 Account
const alice(
"alice");
950 Account
const becky(
"becky");
951 env.fund(XRP(100000), alice, becky);
962 for (
uint256 const& offerIndex : offerIndexes)
968 env(token::burn(alice, nftokenID));
971 uint32_t offerDeletedCount = 0;
973 for (
uint256 const& offerIndex : offerIndexes)
985 BEAST_EXPECT(ownerCount(env, alice) == 1);
990 Env env{*
this, features};
992 Account
const alice(
"alice");
993 Account
const becky(
"becky");
994 env.fund(XRP(100000), alice, becky);
1006 for (
uint256 const& offerIndex : offerIndexes)
1012 env(token::createOffer(becky, nftokenID, drops(1)),
1013 token::owner(alice));
1015 env(token::createOffer(becky, nftokenID, drops(1)),
1016 token::owner(alice));
1020 env(token::burn(alice, nftokenID));
1025 for (
uint256 const& offerIndex : offerIndexes)
1032 BEAST_EXPECT(ownerCount(env, alice) == 0);
1035 BEAST_EXPECT(ownerCount(env, becky) == 1);
1044 if (features[fixNFTokenPageLinks])
1052 using namespace test::jtx;
1054 Account
const alice{
"alice"};
1055 Account
const minter{
"minter"};
1057 Env env{*
this, features};
1058 env.fund(XRP(1000), alice, minter);
1062 auto genPackedTokens = [
this, &env, &alice, &minter]() {
1074 auto internalTaxon = [&env](
1075 Account
const& acct,
1078 env.le(acct)->at(~sfMintedNFTokens).value_or(0);
1081 tokenSeq += env.le(acct)
1082 ->at(~sfFirstNFTokenSequence)
1083 .value_or(env.seq(acct));
1097 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
1098 uint32_t
const extTaxon = internalTaxon(minter, intTaxon);
1105 uint256 const minterOfferIndex =
1107 env(token::createOffer(minter, nfts.
back(), XRP(0)),
1112 env(token::acceptSellOffer(alice, minterOfferIndex));
1123 jvParams[jss::ledger_index] =
"current";
1124 jvParams[jss::binary] =
false;
1127 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
1129 Json::Value& state = jrr[jss::result][jss::state];
1134 if (state[i].isMember(sfNFTokens.jsonName) &&
1135 state[i][sfNFTokens.jsonName].
isArray())
1138 state[i][sfNFTokens.jsonName].
size() == 32);
1144 BEAST_EXPECT(pageCount == 3);
1151 BEAST_EXPECT(
nftCount(env, alice) == 96);
1152 BEAST_EXPECT(ownerCount(env, alice) == 3);
1157 if (!BEAST_EXPECT(lastNFTokenPage))
1160 uint256 const middleNFTokenPageIndex =
1161 lastNFTokenPage->at(sfPreviousPageMin);
1164 if (!BEAST_EXPECT(middleNFTokenPage))
1167 uint256 const firstNFTokenPageIndex =
1168 middleNFTokenPage->at(sfPreviousPageMin);
1169 auto firstNFTokenPage = env.le(
1171 if (!BEAST_EXPECT(firstNFTokenPage))
1176 for (
int i = 0; i < 32; ++i)
1182 uint256 const aliceOfferIndex =
1184 env(token::createOffer(alice, last32NFTs.
back(), XRP(0)),
1189 env(token::acceptSellOffer(minter, aliceOfferIndex));
1197 BEAST_EXPECT(!lastNFTokenPage);
1198 BEAST_EXPECT(ownerCount(env, alice) == 2);
1204 if (!BEAST_EXPECT(middleNFTokenPage))
1206 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
1207 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
1210 auto const acctDelFee{drops(env.current()->fees().increment)};
1211 env(acctdelete(alice, minter),
1217 for (
uint256 nftID : last32NFTs)
1220 uint256 const minterOfferIndex =
1222 env(token::createOffer(minter, nftID, XRP(0)),
1227 env(token::acceptSellOffer(alice, minterOfferIndex));
1230 BEAST_EXPECT(ownerCount(env, alice) == 3);
1238 params[jss::account] = alice.human();
1239 return env.rpc(
"json",
"account_objects",
to_string(params));
1241 BEAST_EXPECT(!acctObjs.
isMember(jss::marker));
1243 acctObjs[jss::result][jss::account_objects].size() == 2);
1250 params[jss::account] = alice.human();
1251 params[jss::type] =
"state";
1252 return env.rpc(
"json",
"account_nfts",
to_string(params));
1254 BEAST_EXPECT(!aliceNFTs.
isMember(jss::marker));
1256 aliceNFTs[jss::result][jss::account_nfts].size() == 64);