20 #include <ripple/app/tx/impl/details/NFTokenUtils.h>
21 #include <ripple/protocol/Feature.h>
22 #include <ripple/protocol/jss.h>
36 if (
auto const sleAcct = env.
le(acct))
46 params[jss::account] = acct.
human();
47 params[jss::type] =
"state";
49 return nfts[jss::result][jss::account_nfts].
size();
59 size_t const tokenCancelCount)
61 using namespace test::jtx;
64 env(token::mint(owner, 0),
69 offerIndexes.
reserve(tokenCancelCount);
71 for (uint32_t i = 0; i < tokenCancelCount; ++i)
75 env(token::createOffer(owner, nftokenID, drops(1)),
87 testcase(
"Burn random");
89 using namespace test::jtx;
91 Env env{*
this, features};
99 AcctStat(
char const* name) : acct(name)
108 AcctStat alice{
"alice"};
109 AcctStat becky{
"becky"};
110 AcctStat minter{
"minter"};
112 env.fund(XRP(10000), alice, becky, minter);
116 env(token::setMinter(alice, minter));
137 alice.nfts.reserve(105);
138 while (alice.nfts.size() < 105)
141 alice.nfts.push_back(token::getNextID(
143 env(token::mint(alice),
145 token::xferFee(xferFee));
149 minter.nfts.reserve(105);
150 while (minter.nfts.size() < 105)
153 minter.nfts.push_back(token::getNextID(
155 env(token::mint(minter),
157 token::xferFee(xferFee),
158 token::issuer(alice));
164 becky.nfts.reserve(70);
166 auto aliceIter = alice.nfts.begin();
167 auto minterIter = minter.nfts.begin();
168 while (becky.nfts.size() < 70)
171 auto xferNFT = [&env, &becky](AcctStat& acct,
auto& iter) {
174 env(token::createOffer(acct, *iter, XRP(0)),
177 env(token::acceptSellOffer(becky, offerIndex));
179 becky.nfts.push_back(*iter);
180 iter = acct.nfts.erase(iter);
183 xferNFT(alice, aliceIter);
184 xferNFT(minter, minterIter);
186 BEAST_EXPECT(aliceIter == alice.nfts.end());
187 BEAST_EXPECT(minterIter == minter.nfts.end());
191 BEAST_EXPECT(
nftCount(env, alice.acct) == 70);
192 BEAST_EXPECT(
nftCount(env, becky.acct) == 70);
193 BEAST_EXPECT(
nftCount(env, minter.acct) == 70);
198 [&env](AcctStat& owner, AcctStat& other1, AcctStat& other2) {
202 env(token::createOffer(owner, nft, drops(1)),
204 token::destination(other1));
205 env(token::createOffer(owner, nft, drops(1)),
207 token::destination(other2));
211 env(token::createOffer(other1, nft, drops(1)),
212 token::owner(owner));
213 env(token::createOffer(other2, nft, drops(1)),
214 token::owner(owner));
217 env(token::createOffer(other2, nft, drops(2)),
218 token::owner(owner));
219 env(token::createOffer(other1, nft, drops(2)),
220 token::owner(owner));
224 addOffers(alice, becky, minter);
225 addOffers(becky, minter, alice);
226 addOffers(minter, alice, becky);
234 AcctStat*
const stats[3] = {&alice, &becky, &minter};
238 while (stats[0]->nfts.size() > 0 || stats[1]->nfts.size() > 0 ||
239 stats[2]->nfts.size() > 0)
243 AcctStat& owner = *(stats[acctDist(engine)]);
244 if (owner.nfts.empty())
249 0lu, owner.nfts.size() - 1);
250 auto nftIter = owner.nfts.begin() + nftDist(engine);
252 owner.nfts.erase(nftIter);
257 AcctStat& burner = owner.acct == becky.acct
258 ? *(stats[acctDist(engine)])
259 : mintDist(engine) ? alice : minter;
261 if (owner.acct == burner.acct)
262 env(token::burn(burner, nft));
264 env(token::burn(burner, nft), token::owner(owner));
269 BEAST_EXPECT(
nftCount(env, alice.acct) == alice.nfts.size());
270 BEAST_EXPECT(
nftCount(env, becky.acct) == becky.nfts.size());
271 BEAST_EXPECT(
nftCount(env, minter.acct) == minter.nfts.size());
273 BEAST_EXPECT(
nftCount(env, alice.acct) == 0);
274 BEAST_EXPECT(
nftCount(env, becky.acct) == 0);
275 BEAST_EXPECT(
nftCount(env, minter.acct) == 0);
291 testcase(
"Burn sequential");
293 using namespace test::jtx;
295 Account
const alice{
"alice"};
297 Env env{*
this, features};
298 env.fund(XRP(1000), alice);
309 [[maybe_unused]]
auto printNFTPages = [&env](Volume vol) {
311 jvParams[jss::ledger_index] =
"current";
312 jvParams[jss::binary] =
false;
317 boost::lexical_cast<std::string>(jvParams));
321 !jrr[jss::result].
isMember(jss::state))
339 std::cout << tokenCount <<
" NFTokens in page "
367 auto genPackedTokens = [
this, &env, &alice](
380 auto internalTaxon = [&env](
389 tokenSeq += env.le(acct)
391 .value_or(env.seq(acct));
405 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
406 uint32_t
const extTaxon = internalTaxon(alice, intTaxon);
407 nfts.push_back(token::getNextID(env, alice, extTaxon));
408 env(token::mint(alice, extTaxon));
419 jvParams[jss::ledger_index] =
"current";
420 jvParams[jss::binary] =
false;
425 boost::lexical_cast<std::string>(jvParams));
442 BEAST_EXPECT(pageCount == 3);
450 genPackedTokens(nfts);
451 BEAST_EXPECT(
nftCount(env, alice) == 96);
454 for (
uint256 const& nft : nfts)
456 env(token::burn(alice, {nft}));
459 BEAST_EXPECT(
nftCount(env, alice) == 0);
463 auto checkNoTokenPages = [
this, &env]() {
465 jvParams[jss::ledger_index] =
"current";
466 jvParams[jss::binary] =
false;
471 boost::lexical_cast<std::string>(jvParams));
486 genPackedTokens(nfts);
487 BEAST_EXPECT(
nftCount(env, alice) == 96);
491 for (
uint256 const& nft : nfts)
493 env(token::burn(alice, {nft}));
496 BEAST_EXPECT(
nftCount(env, alice) == 0);
503 genPackedTokens(nfts);
504 BEAST_EXPECT(
nftCount(env, alice) == 96);
509 env(token::burn(alice, nfts[i]));
512 nfts.erase(nfts.begin() + 32, nfts.begin() + 64);
513 BEAST_EXPECT(
nftCount(env, alice) == 64);
517 for (
uint256 const& nft : nfts)
519 env(token::burn(alice, {nft}));
522 BEAST_EXPECT(
nftCount(env, alice) == 0);
530 testcase(
"Burn too many offers");
532 using namespace test::jtx;
538 Env env{*
this, features};
540 Account
const alice(
"alice");
541 Account
const becky(
"becky");
542 env.fund(XRP(1000), alice, becky);
558 env(token::mint(alice, 0),
568 env.fund(XRP(1000), acct);
573 env(token::createOffer(acct, nftokenID, drops(1)),
574 token::owner(alice));
579 for (
uint256 const& offerIndex : offerIndexes)
585 uint256 const beckyOfferIndex =
587 env(token::createOffer(becky, nftokenID, drops(1)),
588 token::owner(alice));
591 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
595 for (
int i = 0; i < 10; ++i)
600 env(token::cancelOffer(becky, {beckyOfferIndex}));
603 uint256 const aliceOfferIndex =
605 env(token::createOffer(alice, nftokenID, drops(1)),
609 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
613 env(token::cancelOffer(alice, {aliceOfferIndex}));
616 env(token::burn(alice, nftokenID));
620 for (
uint256 const& offerIndex : offerIndexes)
636 Env env{*
this, features};
638 Account
const alice(
"alice");
639 Account
const becky(
"becky");
640 env.fund(XRP(100000), alice, becky);
651 for (
uint256 const& offerIndex : offerIndexes)
657 uint256 const beckyOfferIndex =
659 env(token::createOffer(becky, nftokenID, drops(1)),
660 token::owner(alice));
664 env(token::burn(alice, nftokenID));
669 for (
uint256 const& offerIndex : offerIndexes)
687 Env env{*
this, features};
689 Account
const alice(
"alice");
690 Account
const becky(
"becky");
691 env.fund(XRP(100000), alice, becky);
702 for (
uint256 const& offerIndex : offerIndexes)
708 env(token::burn(alice, nftokenID));
711 uint32_t offerDeletedCount = 0;
713 for (
uint256 const& offerIndex : offerIndexes)
732 Env env{*
this, features};
734 Account
const alice(
"alice");
735 Account
const becky(
"becky");
736 env.fund(XRP(100000), alice, becky);
748 for (
uint256 const& offerIndex : offerIndexes)
754 env(token::createOffer(becky, nftokenID, drops(1)),
755 token::owner(alice));
757 env(token::createOffer(becky, nftokenID, drops(1)),
758 token::owner(alice));
762 env(token::burn(alice, nftokenID));
767 for (
uint256 const& offerIndex : offerIndexes)
793 using namespace test::jtx;