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();
56 testcase(
"Burn random");
58 using namespace test::jtx;
60 Env env{*
this, features};
68 AcctStat(
char const* name) : acct(name)
77 AcctStat alice{
"alice"};
78 AcctStat becky{
"becky"};
79 AcctStat minter{
"minter"};
81 env.fund(XRP(10000), alice, becky, minter);
85 env(token::setMinter(alice, minter));
106 alice.nfts.reserve(105);
107 while (alice.nfts.size() < 105)
110 alice.nfts.push_back(token::getNextID(
112 env(token::mint(alice),
114 token::xferFee(xferFee));
118 minter.nfts.reserve(105);
119 while (minter.nfts.size() < 105)
122 minter.nfts.push_back(token::getNextID(
124 env(token::mint(minter),
126 token::xferFee(xferFee),
127 token::issuer(alice));
133 becky.nfts.reserve(70);
135 auto aliceIter = alice.nfts.begin();
136 auto minterIter = minter.nfts.begin();
137 while (becky.nfts.size() < 70)
140 auto xferNFT = [&env, &becky](AcctStat& acct,
auto& iter) {
143 env(token::createOffer(acct, *iter, XRP(0)),
146 env(token::acceptSellOffer(becky, offerIndex));
148 becky.nfts.push_back(*iter);
149 iter = acct.nfts.erase(iter);
152 xferNFT(alice, aliceIter);
153 xferNFT(minter, minterIter);
155 BEAST_EXPECT(aliceIter == alice.nfts.end());
156 BEAST_EXPECT(minterIter == minter.nfts.end());
160 BEAST_EXPECT(
nftCount(env, alice.acct) == 70);
161 BEAST_EXPECT(
nftCount(env, becky.acct) == 70);
162 BEAST_EXPECT(
nftCount(env, minter.acct) == 70);
167 [&env](AcctStat& owner, AcctStat& other1, AcctStat& other2) {
171 env(token::createOffer(owner, nft, drops(1)),
173 token::destination(other1));
174 env(token::createOffer(owner, nft, drops(1)),
176 token::destination(other2));
180 env(token::createOffer(other1, nft, drops(1)),
181 token::owner(owner));
182 env(token::createOffer(other2, nft, drops(1)),
183 token::owner(owner));
186 env(token::createOffer(other2, nft, drops(2)),
187 token::owner(owner));
188 env(token::createOffer(other1, nft, drops(2)),
189 token::owner(owner));
193 addOffers(alice, becky, minter);
194 addOffers(becky, minter, alice);
195 addOffers(minter, alice, becky);
203 AcctStat*
const stats[3] = {&alice, &becky, &minter};
207 while (stats[0]->nfts.size() > 0 || stats[1]->nfts.size() > 0 ||
208 stats[2]->nfts.size() > 0)
212 AcctStat& owner = *(stats[acctDist(engine)]);
213 if (owner.nfts.empty())
218 0lu, owner.nfts.size() - 1);
219 auto nftIter = owner.nfts.begin() + nftDist(engine);
221 owner.nfts.erase(nftIter);
226 AcctStat& burner = owner.acct == becky.acct
227 ? *(stats[acctDist(engine)])
228 : mintDist(engine) ? alice : minter;
230 if (owner.acct == burner.acct)
231 env(token::burn(burner, nft));
233 env(token::burn(burner, nft), token::owner(owner));
238 BEAST_EXPECT(
nftCount(env, alice.acct) == alice.nfts.size());
239 BEAST_EXPECT(
nftCount(env, becky.acct) == becky.nfts.size());
240 BEAST_EXPECT(
nftCount(env, minter.acct) == minter.nfts.size());
242 BEAST_EXPECT(
nftCount(env, alice.acct) == 0);
243 BEAST_EXPECT(
nftCount(env, becky.acct) == 0);
244 BEAST_EXPECT(
nftCount(env, minter.acct) == 0);
260 testcase(
"Burn sequential");
262 using namespace test::jtx;
264 Account
const alice{
"alice"};
266 Env env{*
this, features};
267 env.fund(XRP(1000), alice);
278 [[maybe_unused]]
auto printNFTPages = [&env](Volume vol) {
280 jvParams[jss::ledger_index] =
"current";
281 jvParams[jss::binary] =
false;
286 boost::lexical_cast<std::string>(jvParams));
290 !jrr[jss::result].
isMember(jss::state))
308 std::cout << tokenCount <<
" NFTokens in page "
336 auto genPackedTokens = [
this, &env, &alice](
349 auto internalTaxon = [&env](
366 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
367 uint32_t
const extTaxon = internalTaxon(alice, intTaxon);
368 nfts.push_back(token::getNextID(env, alice, extTaxon));
369 env(token::mint(alice, extTaxon));
380 jvParams[jss::ledger_index] =
"current";
381 jvParams[jss::binary] =
false;
386 boost::lexical_cast<std::string>(jvParams));
403 BEAST_EXPECT(pageCount == 3);
411 genPackedTokens(nfts);
412 BEAST_EXPECT(
nftCount(env, alice) == 96);
415 for (
uint256 const& nft : nfts)
417 env(token::burn(alice, {nft}));
420 BEAST_EXPECT(
nftCount(env, alice) == 0);
424 auto checkNoTokenPages = [
this, &env]() {
426 jvParams[jss::ledger_index] =
"current";
427 jvParams[jss::binary] =
false;
432 boost::lexical_cast<std::string>(jvParams));
447 genPackedTokens(nfts);
448 BEAST_EXPECT(
nftCount(env, alice) == 96);
452 for (
uint256 const& nft : nfts)
454 env(token::burn(alice, {nft}));
457 BEAST_EXPECT(
nftCount(env, alice) == 0);
464 genPackedTokens(nfts);
465 BEAST_EXPECT(
nftCount(env, alice) == 96);
470 env(token::burn(alice, nfts[i]));
473 nfts.erase(nfts.begin() + 32, nfts.begin() + 64);
474 BEAST_EXPECT(
nftCount(env, alice) == 64);
478 for (
uint256 const& nft : nfts)
480 env(token::burn(alice, {nft}));
483 BEAST_EXPECT(
nftCount(env, alice) == 0);
491 testcase(
"Burn too many offers");
493 using namespace test::jtx;
495 Env env{*
this, features};
497 Account
const alice(
"alice");
498 Account
const becky(
"becky");
499 env.fund(XRP(1000), alice, becky);
515 env(token::mint(alice, 0),
525 env.fund(XRP(1000), acct);
529 env(token::createOffer(acct, nftokenID, drops(1)),
530 token::owner(alice));
535 for (
uint256 const& offerIndex : offerIndexes)
541 uint256 const beckyOfferIndex =
543 env(token::createOffer(becky, nftokenID, drops(1)),
544 token::owner(alice));
547 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
550 for (
int i = 0; i < 10; ++i)
555 env(token::cancelOffer(becky, {beckyOfferIndex}));
558 uint256 const aliceOfferIndex =
560 env(token::createOffer(alice, nftokenID, drops(1)),
564 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
568 env(token::cancelOffer(alice, {aliceOfferIndex}));
571 env(token::burn(alice, nftokenID));
575 for (
uint256 const& offerIndex : offerIndexes)
597 using namespace test::jtx;