22#include <xrpld/app/tx/detail/ApplyContext.h>
23#include <xrpld/app/tx/detail/NFTokenUtils.h>
25#include <xrpl/protocol/Feature.h>
26#include <xrpl/protocol/jss.h>
37 params[jss::account] = acct.
human();
38 params[jss::type] =
"state";
40 return nfts[jss::result][jss::account_nfts].
size();
49 using namespace test::jtx;
62 auto internalTaxon = [
this, &env](
66 auto const le = env.
le(acct);
68 return le->at(~sfMintedNFTokens).value_or(0u);
74 if (env.
current()->rules().enabled(fixNFTokenRemint))
75 tokenSeq += env.
le(acct)
76 ->at(~sfFirstNFTokenSequence)
77 .value_or(env.
seq(acct));
90 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
91 uint32_t
const extTaxon = internalTaxon(owner, intTaxon);
106 params[jss::account] = owner.
human();
107 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
110 resp[jss::result][jss::account_objects];
116 acctObjs[i].isMember(sfNFTokens.jsonName) &&
117 acctObjs[i][sfNFTokens.jsonName].
isArray()))
119 BEAST_EXPECT(acctObjs[i][sfNFTokens.jsonName].
size() == 32);
125 BEAST_EXPECT(pageCount == 3);
133 testcase(
"LedgerStateFix error cases");
135 using namespace test::jtx;
137 Account
const alice(
"alice");
142 Env env{*
this, supported_amendments() - fixNFTokenPageLinks};
143 env.fund(XRP(1000), alice);
145 auto const linkFixFee = drops(env.current()->fees().increment);
146 env(ledgerStateFix::nftPageLinks(alice, alice),
151 Env env{*
this, supported_amendments()};
152 env.fund(XRP(1000), alice);
154 env(ticket::create(alice, 1));
160 Json::Value tx = ledgerStateFix::nftPageLinks(alice, alice);
161 tx[sfAccountTxnID.jsonName] =
162 "00000000000000000000000000000000"
163 "00000000000000000000000000000000";
164 env(tx, ticket::use(ticketSeq), ter(
temINVALID));
167 env(ledgerStateFix::nftPageLinks(alice, alice), ter(
telINSUF_FEE_P));
170 auto const linkFixFee = drops(env.current()->fees().increment);
171 env(ledgerStateFix::nftPageLinks(alice, alice),
178 Json::Value tx = ledgerStateFix::nftPageLinks(alice, alice);
184 Json::Value tx = ledgerStateFix::nftPageLinks(alice, alice);
185 tx[sfLedgerFixType.jsonName] = 0;
188 tx[sfLedgerFixType.jsonName] = 200;
193 Account
const carol(
"carol");
195 env(ledgerStateFix::nftPageLinks(alice, carol),
203 testcase(
"NFTokenPageLinkFix error cases");
205 using namespace test::jtx;
207 Account
const alice(
"alice");
209 Env env{*
this, supported_amendments()};
210 env.fund(XRP(1000), alice);
217 auto const linkFixFee = drops(env.current()->fees().increment);
218 env(ledgerStateFix::nftPageLinks(alice, alice),
226 env(ledgerStateFix::nftPageLinks(alice, alice),
237 env(ledgerStateFix::nftPageLinks(alice, alice),
255 using namespace test::jtx;
257 Account
const alice(
"alice");
258 Account
const bob(
"bob");
259 Account
const carol(
"carol");
260 Account
const daria(
"daria");
262 Env env{*
this, supported_amendments() - fixNFTokenPageLinks};
263 env.fund(XRP(1000), alice, bob, carol, daria);
272 BEAST_EXPECT(
nftCount(env, alice) == 96);
273 BEAST_EXPECT(ownerCount(env, alice) == 3);
276 uint256 const aliceMiddleNFTokenPageIndex = [&env, &alice]() {
278 return lastNFTokenPage->at(sfPreviousPageMin);
282 for (
int i = 0; i < 32; ++i)
284 env(token::burn(alice, {aliceNFTs[i]}));
288 for (
int i = 0; i < 32; ++i)
290 env(token::burn(alice, {aliceNFTs.
back()}));
294 BEAST_EXPECT(ownerCount(env, alice) == 1);
295 BEAST_EXPECT(
nftCount(env, alice) == 32);
306 if (!BEAST_EXPECT(aliceMiddleNFTokenPage))
310 !aliceMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
312 !aliceMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
322 BEAST_EXPECT(
nftCount(env, bob) == 96);
323 BEAST_EXPECT(ownerCount(env, bob) == 3);
326 uint256 const bobMiddleNFTokenPageIndex = [&env, &bob]() {
328 return lastNFTokenPage->at(sfPreviousPageMin);
332 for (
int i = 0; i < 32; ++i)
334 env(token::burn(bob, {bobNFTs.
back()}));
338 BEAST_EXPECT(
nftCount(env, bob) == 64);
339 BEAST_EXPECT(ownerCount(env, bob) == 2);
351 if (!BEAST_EXPECT(bobMiddleNFTokenPage))
355 bobMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
356 BEAST_EXPECT(!bobMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
366 BEAST_EXPECT(
nftCount(env, carol) == 96);
367 BEAST_EXPECT(ownerCount(env, carol) == 3);
370 uint256 const carolMiddleNFTokenPageIndex = [&env, &carol]() {
372 return lastNFTokenPage->at(sfPreviousPageMin);
378 for (
int i = 0; i < 32; ++i)
382 env(token::createOffer(carol, carolNFTs.
back(), XRP(0)),
386 env(token::acceptSellOffer(daria, offerIndex));
392 BEAST_EXPECT(
nftCount(env, carol) == 64);
393 BEAST_EXPECT(ownerCount(env, carol) == 2);
404 if (!BEAST_EXPECT(carolMiddleNFTokenPage))
407 BEAST_EXPECT(carolMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
408 BEAST_EXPECT(!carolMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
414 for (
uint256 const& nft : dariaNFTs)
418 env(token::createOffer(carol, nft, drops(1)), token::owner(daria));
421 env(token::acceptBuyOffer(daria, offerIndex));
429 BEAST_EXPECT(
nftCount(env, carol) == 64);
430 BEAST_EXPECT(ownerCount(env, carol) == 3);
436 if (!BEAST_EXPECT(carolMiddleNFTokenPage))
440 carolMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
442 !carolMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
449 !carolLastNFTokenPage->isFieldPresent(sfPreviousPageMin));
450 BEAST_EXPECT(!carolLastNFTokenPage->isFieldPresent(sfNextPageMin));
457 auto const linkFixFee = drops(env.current()->fees().increment);
458 env(ledgerStateFix::nftPageLinks(daria, alice),
464 for (
int i = 0; i < 15; ++i)
467 env.enableFeature(fixNFTokenPageLinks);
483 if (!BEAST_EXPECT(aliceMiddleNFTokenPage))
487 !aliceMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
489 !aliceMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
498 env(ledgerStateFix::nftPageLinks(daria, alice), fee(linkFixFee));
504 if (!BEAST_EXPECT(aliceLastNFTokenPage))
508 !aliceLastNFTokenPage->isFieldPresent(sfPreviousPageMin));
509 BEAST_EXPECT(!aliceLastNFTokenPage->isFieldPresent(sfNextPageMin));
516 BEAST_EXPECT(
nftCount(env, alice) == 32);
517 BEAST_EXPECT(ownerCount(env, alice) == 1);
532 if (!BEAST_EXPECT(bobMiddleNFTokenPage))
536 bobMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
537 BEAST_EXPECT(!bobMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
541 env(ledgerStateFix::nftPageLinks(daria, bob), fee(linkFixFee));
548 auto const bobLastNFTokenPage = env.le(lastPageKeylet);
549 if (!BEAST_EXPECT(bobLastNFTokenPage))
552 BEAST_EXPECT(bobLastNFTokenPage->isFieldPresent(sfPreviousPageMin));
554 bobLastNFTokenPage->at(sfPreviousPageMin) !=
555 bobMiddleNFTokenPageIndex);
556 BEAST_EXPECT(!bobLastNFTokenPage->isFieldPresent(sfNextPageMin));
560 bobLastNFTokenPage->at(sfPreviousPageMin)));
561 if (!BEAST_EXPECT(bobNewFirstNFTokenPage))
565 bobNewFirstNFTokenPage->isFieldPresent(sfNextPageMin) &&
566 bobNewFirstNFTokenPage->at(sfNextPageMin) ==
569 !bobNewFirstNFTokenPage->isFieldPresent(sfPreviousPageMin));
576 BEAST_EXPECT(
nftCount(env, bob) == 64);
577 BEAST_EXPECT(ownerCount(env, bob) == 2);
589 if (!BEAST_EXPECT(carolMiddleNFTokenPage))
593 carolMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
595 !carolMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
602 !carolLastNFTokenPage->isFieldPresent(sfPreviousPageMin));
603 BEAST_EXPECT(!carolLastNFTokenPage->isFieldPresent(sfNextPageMin));
607 env(ledgerStateFix::nftPageLinks(carol, carol), fee(linkFixFee));
615 if (!BEAST_EXPECT(carolMiddleNFTokenPage))
619 carolMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
621 carolMiddleNFTokenPage->isFieldPresent(sfNextPageMin) &&
622 carolMiddleNFTokenPage->at(sfNextPageMin) ==
626 auto carolLastNFTokenPage = env.le(lastPageKeylet);
627 if (!BEAST_EXPECT(carolLastNFTokenPage))
631 carolLastNFTokenPage->isFieldPresent(sfPreviousPageMin) &&
632 carolLastNFTokenPage->at(sfPreviousPageMin) ==
633 carolMiddleNFTokenPageIndex);
634 BEAST_EXPECT(!carolLastNFTokenPage->isFieldPresent(sfNextPageMin));
639 carolMiddleNFTokenPage->at(sfPreviousPageMin)));
640 if (!BEAST_EXPECT(carolFirstNFTokenPage))
644 carolFirstNFTokenPage->isFieldPresent(sfNextPageMin) &&
645 carolFirstNFTokenPage->at(sfNextPageMin) ==
646 carolMiddleNFTokenPageIndex);
648 !carolFirstNFTokenPage->isFieldPresent(sfPreviousPageMin));
652 BEAST_EXPECT(
nftCount(env, carol) == 96);
653 BEAST_EXPECT(ownerCount(env, carol) == 3);
666BEAST_DEFINE_TESTSUITE(FixNFTokenPageLinks, tx,
ripple);
UInt size() const
Number of values in array or object.
Value removeMember(const char *key)
Remove and return the named member.
testcase_t testcase
Memberspace for declaring test cases.
void testFixNFTokenPageLinks()
std::vector< uint256 > genPackedTokens(test::jtx::Env &env, test::jtx::Account const &owner)
void run() override
Runs the suite.
void testTokenPageLinkErrors()
static std::uint32_t nftCount(test::jtx::Env &env, test::jtx::Account const &acct)
void testLedgerStateFixErrors()
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Keylet nftpage(Keylet const &k, uint256 const &token)
Keylet nftpage_min(AccountID const &owner)
NFT page keylets.
Keylet nftpage_max(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Taxon cipheredTaxon(std::uint32_t tokenSeq, Taxon taxon)
Taxon toTaxon(std::uint32_t i)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::uint32_t const tfSellNFToken
constexpr std::uint32_t tfPassive
@ tefINVALID_LEDGER_FIX_TYPE
std::string to_string(base_uint< Bits, Tag > const &a)
constexpr std::uint32_t const tfTransferable