123 using namespace test::jtx;
125 Env env{*
this, features};
133 AcctStat(
char const* name) : acct(name)
142 AcctStat alice{
"alice"};
143 AcctStat becky{
"becky"};
144 AcctStat minter{
"minter"};
146 env.fund(XRP(10000), alice, becky, minter);
150 env(token::setMinter(alice, minter));
171 alice.nfts.reserve(105);
172 while (alice.nfts.size() < 105)
175 alice.nfts.push_back(
176 token::getNextID(env, alice, 0u, tfTransferable | tfBurnable, xferFee));
177 env(token::mint(alice), txflags(tfTransferable | tfBurnable), token::xferFee(xferFee));
181 minter.nfts.reserve(105);
182 while (minter.nfts.size() < 105)
185 minter.nfts.push_back(
186 token::getNextID(env, alice, 0u, tfTransferable | tfBurnable, xferFee));
187 env(token::mint(minter),
188 txflags(tfTransferable | tfBurnable),
189 token::xferFee(xferFee),
190 token::issuer(alice));
196 becky.nfts.reserve(70);
198 auto aliceIter = alice.nfts.begin();
199 auto minterIter = minter.nfts.begin();
200 while (becky.nfts.size() < 70)
203 auto xferNFT = [&env, &becky](AcctStat& acct,
auto& iter) {
205 env(token::createOffer(acct, *iter, XRP(0)), txflags(tfSellNFToken));
207 env(token::acceptSellOffer(becky, offerIndex));
209 becky.nfts.push_back(*iter);
210 iter = acct.nfts.erase(iter);
213 xferNFT(alice, aliceIter);
214 xferNFT(minter, minterIter);
216 BEAST_EXPECT(aliceIter == alice.nfts.end());
217 BEAST_EXPECT(minterIter == minter.nfts.end());
221 BEAST_EXPECT(
nftCount(env, alice.acct) == 70);
222 BEAST_EXPECT(
nftCount(env, becky.acct) == 70);
223 BEAST_EXPECT(
nftCount(env, minter.acct) == 70);
227 auto addOffers = [&env](AcctStat& owner, AcctStat& other1, AcctStat& other2) {
228 for (
uint256 const nft : owner.nfts)
231 env(token::createOffer(owner, nft, drops(1)),
232 txflags(tfSellNFToken),
233 token::destination(other1));
234 env(token::createOffer(owner, nft, drops(1)),
235 txflags(tfSellNFToken),
236 token::destination(other2));
240 env(token::createOffer(other1, nft, drops(1)), token::owner(owner));
241 env(token::createOffer(other2, nft, drops(1)), token::owner(owner));
244 env(token::createOffer(other2, nft, drops(2)), token::owner(owner));
245 env(token::createOffer(other1, nft, drops(2)), token::owner(owner));
249 addOffers(alice, becky, minter);
250 addOffers(becky, minter, alice);
251 addOffers(minter, alice, becky);
252 BEAST_EXPECT(ownerCount(env, alice) == 424);
253 BEAST_EXPECT(ownerCount(env, becky) == 424);
254 BEAST_EXPECT(ownerCount(env, minter) == 424);
259 AcctStat*
const stats[3] = {&alice, &becky, &minter};
263 while (!stats[0]->nfts.empty() || !stats[1]->nfts.empty() || !stats[2]->nfts.empty())
267 AcctStat& owner = *(stats[acctDist(engine)]);
268 if (owner.nfts.empty())
273 auto nftIter = owner.nfts.begin() + nftDist(engine);
275 owner.nfts.erase(nftIter);
280 AcctStat
const& burner = [&]() -> AcctStat& {
281 if (owner.acct == becky.acct)
282 return *(stats[acctDist(engine)]);
283 return mintDist(engine) ? alice : minter;
286 if (owner.acct == burner.acct)
288 env(token::burn(burner, nft));
292 env(token::burn(burner, nft), token::owner(owner));
298 BEAST_EXPECT(
nftCount(env, alice.acct) == alice.nfts.size());
299 BEAST_EXPECT(
nftCount(env, becky.acct) == becky.nfts.size());
300 BEAST_EXPECT(
nftCount(env, minter.acct) == minter.nfts.size());
302 BEAST_EXPECT(
nftCount(env, alice.acct) == 0);
303 BEAST_EXPECT(
nftCount(env, becky.acct) == 0);
304 BEAST_EXPECT(
nftCount(env, minter.acct) == 0);
308 BEAST_EXPECT(ownerCount(env, alice) == 0);
309 BEAST_EXPECT(ownerCount(env, becky) == 0);
310 BEAST_EXPECT(ownerCount(env, minter) == 0);
322 using namespace test::jtx;
324 Account
const alice{
"alice"};
326 Env env{*
this, features};
327 env.fund(XRP(1000), alice);
331 auto genPackedTokens = [
this, &env, &alice]() {
344 std::uint32_t tokenSeq = env.le(acct)->at(~sfMintedNFTokens).value_or(0);
347 tokenSeq += env.le(acct)->at(~sfFirstNFTokenSequence).value_or(env.seq(acct));
360 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
361 uint32_t
const extTaxon = internalTaxon(alice, intTaxon);
362 nfts.
push_back(token::getNextID(env, alice, extTaxon));
363 env(token::mint(alice, extTaxon));
374 jvParams[jss::ledger_index] =
"current";
375 jvParams[jss::binary] =
false;
384 if (state[i].isMember(sfNFTokens.jsonName) &&
385 state[i][sfNFTokens.jsonName].
isArray())
387 BEAST_EXPECT(state[i][sfNFTokens.jsonName].
size() == 32);
393 BEAST_EXPECT(pageCount == 3);
402 BEAST_EXPECT(
nftCount(env, alice) == 96);
403 BEAST_EXPECT(ownerCount(env, alice) == 3);
405 for (
uint256 const& nft : nfts)
407 env(token::burn(alice, {nft}));
410 BEAST_EXPECT(
nftCount(env, alice) == 0);
411 BEAST_EXPECT(ownerCount(env, alice) == 0);
415 auto checkNoTokenPages = [
this, &env]() {
417 jvParams[jss::ledger_index] =
"current";
418 jvParams[jss::binary] =
false;
426 BEAST_EXPECT(!state[i].isMember(sfNFTokens.jsonName));
436 BEAST_EXPECT(
nftCount(env, alice) == 96);
437 BEAST_EXPECT(ownerCount(env, alice) == 3);
442 if (!BEAST_EXPECT(lastNFTokenPage))
445 uint256 const middleNFTokenPageIndex = lastNFTokenPage->at(sfPreviousPageMin);
446 auto middleNFTokenPage =
448 if (!BEAST_EXPECT(middleNFTokenPage))
451 uint256 const firstNFTokenPageIndex = middleNFTokenPage->at(sfPreviousPageMin);
452 auto firstNFTokenPage =
454 if (!BEAST_EXPECT(firstNFTokenPage))
458 for (
int i = 0; i < 31; ++i)
460 env(token::burn(alice, {nfts.
back()}));
468 if (!BEAST_EXPECT(lastNFTokenPage))
471 BEAST_EXPECT(lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
472 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
473 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
476 env(token::burn(alice, {nfts.
back()}));
480 if (features[fixNFTokenPageLinks])
487 BEAST_EXPECT(lastNFTokenPage);
488 BEAST_EXPECT(lastNFTokenPage->at(~sfPreviousPageMin) == firstNFTokenPageIndex);
489 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
490 BEAST_EXPECT(lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
495 BEAST_EXPECT(!middleNFTokenPage);
501 BEAST_EXPECT(firstNFTokenPage);
502 BEAST_EXPECT(!firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
503 BEAST_EXPECT(firstNFTokenPage->at(~sfNextPageMin) == lastNFTokenPage->key());
504 BEAST_EXPECT(lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
512 BEAST_EXPECT(!lastNFTokenPage);
518 if (!BEAST_EXPECT(middleNFTokenPage))
520 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
521 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
525 while (!nfts.
empty())
527 env(token::burn(alice, {nfts.
back()}));
531 BEAST_EXPECT(
nftCount(env, alice) == 0);
532 BEAST_EXPECT(ownerCount(env, alice) == 0);
540 BEAST_EXPECT(
nftCount(env, alice) == 96);
541 BEAST_EXPECT(ownerCount(env, alice) == 3);
546 if (!BEAST_EXPECT(lastNFTokenPage))
549 uint256 const middleNFTokenPageIndex = lastNFTokenPage->at(sfPreviousPageMin);
550 auto middleNFTokenPage =
552 if (!BEAST_EXPECT(middleNFTokenPage))
555 uint256 const firstNFTokenPageIndex = middleNFTokenPage->at(sfPreviousPageMin);
556 auto firstNFTokenPage =
558 if (!BEAST_EXPECT(firstNFTokenPage))
563 env(token::burn(alice, nfts[i]));
567 BEAST_EXPECT(
nftCount(env, alice) == 64);
568 BEAST_EXPECT(ownerCount(env, alice) == 2);
574 BEAST_EXPECT(!middleNFTokenPage);
577 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
578 BEAST_EXPECT(lastNFTokenPage->getFieldH256(sfPreviousPageMin) == firstNFTokenPageIndex);
584 BEAST_EXPECT(!firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
587 for (
uint256 const& nft : nfts)
589 env(token::burn(alice, {nft}));
592 BEAST_EXPECT(
nftCount(env, alice) == 0);
593 BEAST_EXPECT(ownerCount(env, alice) == 0);
601 BEAST_EXPECT(
nftCount(env, alice) == 96);
602 BEAST_EXPECT(ownerCount(env, alice) == 3);
607 if (!BEAST_EXPECT(lastNFTokenPage))
610 uint256 const middleNFTokenPageIndex = lastNFTokenPage->at(sfPreviousPageMin);
611 auto middleNFTokenPage =
613 if (!BEAST_EXPECT(middleNFTokenPage))
616 uint256 const firstNFTokenPageIndex = middleNFTokenPage->at(sfPreviousPageMin);
617 auto firstNFTokenPage =
619 if (!BEAST_EXPECT(firstNFTokenPage))
624 for (
int i = 0; i < 32; ++i)
626 env(token::burn(alice, {nfts.
back()}));
634 BEAST_EXPECT(!firstNFTokenPage);
639 if (!BEAST_EXPECT(middleNFTokenPage))
641 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
642 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfNextPageMin));
645 if (!BEAST_EXPECT(lastNFTokenPage))
647 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
648 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
652 for (
int i = 0; i < 32; ++i)
654 env(token::burn(alice, {nfts.
back()}));
659 if (features[fixNFTokenPageLinks])
666 BEAST_EXPECT(lastNFTokenPage);
667 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
668 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
669 BEAST_EXPECT(lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
674 BEAST_EXPECT(!middleNFTokenPage);
679 BEAST_EXPECT(!firstNFTokenPage);
687 BEAST_EXPECT(!lastNFTokenPage);
693 if (!BEAST_EXPECT(middleNFTokenPage))
695 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
696 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
700 while (!nfts.
empty())
702 env(token::burn(alice, {nfts.
back()}));
706 BEAST_EXPECT(
nftCount(env, alice) == 0);
707 BEAST_EXPECT(ownerCount(env, alice) == 0);
711 if (features[fixNFTokenPageLinks])
725 BEAST_EXPECT(
nftCount(env, alice) == 96);
726 BEAST_EXPECT(ownerCount(env, alice) == 3);
729 for (
int i = 0; i < 31; ++i)
731 env(token::burn(alice, {nfts.
back()}));
747 if (!BEAST_EXPECT(lastNFTokenPage))
749 BEAST_EXPECT(lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
752 ac.view().erase(lastNFTokenPage);
758 terActual = ac.checkInvariants(terActual,
XRPAmount{});
759 BEAST_EXPECT(terExpect == terActual);
760 BEAST_EXPECT(sink.messages().str().starts_with(
"Invariant failed:"));
764 sink.messages().str().find(
765 "Last NFT page deleted with non-empty directory") != std::string::npos);
780 auto middleNFTokenPage = ac.view().peek(
783 lastNFTokenPage->getFieldH256(sfPreviousPageMin)));
784 BEAST_EXPECT(middleNFTokenPage);
788 middleNFTokenPage->makeFieldAbsent(sfNextPageMin);
789 ac.view().update(middleNFTokenPage);
795 terActual = ac.checkInvariants(terActual,
XRPAmount{});
796 BEAST_EXPECT(terExpect == terActual);
797 BEAST_EXPECT(sink.messages().str().starts_with(
"Invariant failed:"));
801 sink.messages().str().find(
"Lost NextMinPage link") != std::string::npos);
813 using namespace test::jtx;
819 Env env{*
this, features};
821 Account
const alice(
"alice");
822 Account
const becky(
"becky");
823 env.fund(XRP(100000), alice, becky);
830 auto const nftokenID =
834 for (
uint256 const& offerIndex : offerIndexes)
841 env(token::createOffer(becky, nftokenID, drops(1)), token::owner(alice));
845 env(token::burn(alice, nftokenID));
850 for (
uint256 const& offerIndex : offerIndexes)
860 BEAST_EXPECT(ownerCount(env, alice) == 0);
861 BEAST_EXPECT(ownerCount(env, becky) == 0);
866 Env env{*
this, features};
868 Account
const alice(
"alice");
869 Account
const becky(
"becky");
870 env.fund(XRP(100000), alice, becky);
877 auto const nftokenID =
881 for (
uint256 const& offerIndex : offerIndexes)
887 env(token::burn(alice, nftokenID));
890 uint32_t offerDeletedCount = 0;
892 for (
uint256 const& offerIndex : offerIndexes)
904 BEAST_EXPECT(ownerCount(env, alice) == 1);
909 Env env{*
this, features};
911 Account
const alice(
"alice");
912 Account
const becky(
"becky");
913 env.fund(XRP(100000), alice, becky);
921 auto const nftokenID =
925 for (
uint256 const& offerIndex : offerIndexes)
931 env(token::createOffer(becky, nftokenID, drops(1)), token::owner(alice));
933 env(token::createOffer(becky, nftokenID, drops(1)), token::owner(alice));
937 env(token::burn(alice, nftokenID));
942 for (
uint256 const& offerIndex : offerIndexes)
949 BEAST_EXPECT(ownerCount(env, alice) == 0);
952 BEAST_EXPECT(ownerCount(env, becky) == 1);
961 if (features[fixNFTokenPageLinks])
969 using namespace test::jtx;
971 Account
const alice{
"alice"};
972 Account
const minter{
"minter"};
974 Env env{*
this, features};
975 env.fund(XRP(1000), alice, minter);
979 auto genPackedTokens = [
this, &env, &alice, &minter]() {
992 std::uint32_t tokenSeq = env.le(acct)->at(~sfMintedNFTokens).value_or(0);
995 tokenSeq += env.le(acct)->at(~sfFirstNFTokenSequence).value_or(env.seq(acct));
1008 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
1009 uint32_t
const extTaxon = internalTaxon(minter, intTaxon);
1010 nfts.
push_back(token::getNextID(env, minter, extTaxon, tfTransferable));
1011 env(token::mint(minter, extTaxon), txflags(tfTransferable));
1016 env(token::createOffer(minter, nfts.
back(), XRP(0)), txflags(tfSellNFToken));
1020 env(token::acceptSellOffer(alice, minterOfferIndex));
1031 jvParams[jss::ledger_index] =
"current";
1032 jvParams[jss::binary] =
false;
1036 Json::Value& state = jrr[jss::result][jss::state];
1041 if (state[i].isMember(sfNFTokens.jsonName) &&
1042 state[i][sfNFTokens.jsonName].
isArray())
1044 BEAST_EXPECT(state[i][sfNFTokens.jsonName].
size() == 32);
1050 BEAST_EXPECT(pageCount == 3);
1057 BEAST_EXPECT(
nftCount(env, alice) == 96);
1058 BEAST_EXPECT(ownerCount(env, alice) == 3);
1063 if (!BEAST_EXPECT(lastNFTokenPage))
1066 uint256 const middleNFTokenPageIndex = lastNFTokenPage->at(sfPreviousPageMin);
1067 auto middleNFTokenPage =
1069 if (!BEAST_EXPECT(middleNFTokenPage))
1072 uint256 const firstNFTokenPageIndex = middleNFTokenPage->at(sfPreviousPageMin);
1073 auto firstNFTokenPage =
1075 if (!BEAST_EXPECT(firstNFTokenPage))
1080 for (
int i = 0; i < 32; ++i)
1087 env(token::createOffer(alice, last32NFTs.
back(), XRP(0)), txflags(tfSellNFToken));
1091 env(token::acceptSellOffer(minter, aliceOfferIndex));
1099 BEAST_EXPECT(!lastNFTokenPage);
1100 BEAST_EXPECT(ownerCount(env, alice) == 2);
1106 if (!BEAST_EXPECT(middleNFTokenPage))
1108 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
1109 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
1112 auto const acctDelFee{drops(env.current()->fees().increment)};
1117 for (
uint256 const nftID : last32NFTs)
1121 env(token::createOffer(minter, nftID, XRP(0)), txflags(tfSellNFToken));
1125 env(token::acceptSellOffer(alice, minterOfferIndex));
1128 BEAST_EXPECT(ownerCount(env, alice) == 3);
1136 params[jss::account] = alice.human();
1137 return env.rpc(
"json",
"account_objects",
to_string(params));
1139 BEAST_EXPECT(!acctObjs.
isMember(jss::marker));
1140 BEAST_EXPECT(acctObjs[jss::result][jss::account_objects].size() == 2);
1147 params[jss::account] = alice.human();
1148 params[jss::type] =
"state";
1149 return env.rpc(
"json",
"account_nfts",
to_string(params));
1151 BEAST_EXPECT(!aliceNFTs.
isMember(jss::marker));
1152 BEAST_EXPECT(aliceNFTs[jss::result][jss::account_nfts].size() == 64);