21#include <xrpld/app/tx/detail/ApplyContext.h>
22#include <xrpld/app/tx/detail/NFTokenUtils.h>
23#include <xrpl/protocol/Feature.h>
24#include <xrpl/protocol/jss.h>
35 params[jss::account] = acct.
human();
36 params[jss::type] =
"state";
38 return nfts[jss::result][jss::account_nfts].
size();
47 using namespace test::jtx;
60 auto internalTaxon = [
this, &env](
64 auto const le = env.
le(acct);
66 return le->at(~sfMintedNFTokens).value_or(0u);
72 if (env.
current()->rules().enabled(fixNFTokenRemint))
73 tokenSeq += env.
le(acct)
74 ->at(~sfFirstNFTokenSequence)
75 .value_or(env.
seq(acct));
88 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
89 uint32_t
const extTaxon = internalTaxon(owner, intTaxon);
104 params[jss::account] = owner.
human();
105 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
108 resp[jss::result][jss::account_objects];
114 acctObjs[i].isMember(sfNFTokens.jsonName) &&
115 acctObjs[i][sfNFTokens.jsonName].
isArray()))
117 BEAST_EXPECT(acctObjs[i][sfNFTokens.jsonName].
size() == 32);
123 BEAST_EXPECT(pageCount == 3);
131 testcase(
"LedgerStateFix error cases");
133 using namespace test::jtx;
135 Account
const alice(
"alice");
140 Env env{*
this, supported_amendments() - fixNFTokenPageLinks};
141 env.fund(XRP(1000), alice);
143 auto const linkFixFee = drops(env.current()->fees().increment);
144 env(ledgerStateFix::nftPageLinks(alice, alice),
149 Env env{*
this, supported_amendments()};
150 env.fund(XRP(1000), alice);
152 env(ticket::create(alice, 1));
158 Json::Value tx = ledgerStateFix::nftPageLinks(alice, alice);
159 tx[sfAccountTxnID.jsonName] =
160 "00000000000000000000000000000000"
161 "00000000000000000000000000000000";
162 env(tx, ticket::use(ticketSeq), ter(
temINVALID));
165 env(ledgerStateFix::nftPageLinks(alice, alice), ter(
telINSUF_FEE_P));
168 auto const linkFixFee = drops(env.current()->fees().increment);
169 env(ledgerStateFix::nftPageLinks(alice, alice),
176 Json::Value tx = ledgerStateFix::nftPageLinks(alice, alice);
182 Json::Value tx = ledgerStateFix::nftPageLinks(alice, alice);
183 tx[sfLedgerFixType.jsonName] = 0;
186 tx[sfLedgerFixType.jsonName] = 200;
191 Account
const carol(
"carol");
193 env(ledgerStateFix::nftPageLinks(alice, carol),
201 testcase(
"NFTokenPageLinkFix error cases");
203 using namespace test::jtx;
205 Account
const alice(
"alice");
207 Env env{*
this, supported_amendments()};
208 env.fund(XRP(1000), alice);
215 auto const linkFixFee = drops(env.current()->fees().increment);
216 env(ledgerStateFix::nftPageLinks(alice, alice),
224 env(ledgerStateFix::nftPageLinks(alice, alice),
235 env(ledgerStateFix::nftPageLinks(alice, alice),
253 using namespace test::jtx;
255 Account
const alice(
"alice");
256 Account
const bob(
"bob");
257 Account
const carol(
"carol");
258 Account
const daria(
"daria");
260 Env env{*
this, supported_amendments() - fixNFTokenPageLinks};
261 env.fund(XRP(1000), alice, bob, carol, daria);
270 BEAST_EXPECT(
nftCount(env, alice) == 96);
271 BEAST_EXPECT(ownerCount(env, alice) == 3);
274 uint256 const aliceMiddleNFTokenPageIndex = [&env, &alice]() {
276 return lastNFTokenPage->at(sfPreviousPageMin);
280 for (
int i = 0; i < 32; ++i)
282 env(token::burn(alice, {aliceNFTs[i]}));
286 for (
int i = 0; i < 32; ++i)
288 env(token::burn(alice, {aliceNFTs.
back()}));
292 BEAST_EXPECT(ownerCount(env, alice) == 1);
293 BEAST_EXPECT(
nftCount(env, alice) == 32);
304 if (!BEAST_EXPECT(aliceMiddleNFTokenPage))
308 !aliceMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
310 !aliceMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
320 BEAST_EXPECT(
nftCount(env, bob) == 96);
321 BEAST_EXPECT(ownerCount(env, bob) == 3);
324 uint256 const bobMiddleNFTokenPageIndex = [&env, &bob]() {
326 return lastNFTokenPage->at(sfPreviousPageMin);
330 for (
int i = 0; i < 32; ++i)
332 env(token::burn(bob, {bobNFTs.
back()}));
336 BEAST_EXPECT(
nftCount(env, bob) == 64);
337 BEAST_EXPECT(ownerCount(env, bob) == 2);
349 if (!BEAST_EXPECT(bobMiddleNFTokenPage))
353 bobMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
354 BEAST_EXPECT(!bobMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
364 BEAST_EXPECT(
nftCount(env, carol) == 96);
365 BEAST_EXPECT(ownerCount(env, carol) == 3);
368 uint256 const carolMiddleNFTokenPageIndex = [&env, &carol]() {
370 return lastNFTokenPage->at(sfPreviousPageMin);
376 for (
int i = 0; i < 32; ++i)
380 env(token::createOffer(carol, carolNFTs.
back(), XRP(0)),
384 env(token::acceptSellOffer(daria, offerIndex));
390 BEAST_EXPECT(
nftCount(env, carol) == 64);
391 BEAST_EXPECT(ownerCount(env, carol) == 2);
402 if (!BEAST_EXPECT(carolMiddleNFTokenPage))
405 BEAST_EXPECT(carolMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
406 BEAST_EXPECT(!carolMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
412 for (
uint256 const& nft : dariaNFTs)
416 env(token::createOffer(carol, nft, drops(1)), token::owner(daria));
419 env(token::acceptBuyOffer(daria, offerIndex));
427 BEAST_EXPECT(
nftCount(env, carol) == 64);
428 BEAST_EXPECT(ownerCount(env, carol) == 3);
434 if (!BEAST_EXPECT(carolMiddleNFTokenPage))
438 carolMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
440 !carolMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
447 !carolLastNFTokenPage->isFieldPresent(sfPreviousPageMin));
448 BEAST_EXPECT(!carolLastNFTokenPage->isFieldPresent(sfNextPageMin));
455 auto const linkFixFee = drops(env.current()->fees().increment);
456 env(ledgerStateFix::nftPageLinks(daria, alice),
462 for (
int i = 0; i < 15; ++i)
465 env.enableFeature(fixNFTokenPageLinks);
481 if (!BEAST_EXPECT(aliceMiddleNFTokenPage))
485 !aliceMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
487 !aliceMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
496 env(ledgerStateFix::nftPageLinks(daria, alice), fee(linkFixFee));
502 if (!BEAST_EXPECT(aliceLastNFTokenPage))
506 !aliceLastNFTokenPage->isFieldPresent(sfPreviousPageMin));
507 BEAST_EXPECT(!aliceLastNFTokenPage->isFieldPresent(sfNextPageMin));
514 BEAST_EXPECT(
nftCount(env, alice) == 32);
515 BEAST_EXPECT(ownerCount(env, alice) == 1);
530 if (!BEAST_EXPECT(bobMiddleNFTokenPage))
534 bobMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
535 BEAST_EXPECT(!bobMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
539 env(ledgerStateFix::nftPageLinks(daria, bob), fee(linkFixFee));
546 auto const bobLastNFTokenPage = env.le(lastPageKeylet);
547 if (!BEAST_EXPECT(bobLastNFTokenPage))
550 BEAST_EXPECT(bobLastNFTokenPage->isFieldPresent(sfPreviousPageMin));
552 bobLastNFTokenPage->at(sfPreviousPageMin) !=
553 bobMiddleNFTokenPageIndex);
554 BEAST_EXPECT(!bobLastNFTokenPage->isFieldPresent(sfNextPageMin));
558 bobLastNFTokenPage->at(sfPreviousPageMin)));
559 if (!BEAST_EXPECT(bobNewFirstNFTokenPage))
563 bobNewFirstNFTokenPage->isFieldPresent(sfNextPageMin) &&
564 bobNewFirstNFTokenPage->at(sfNextPageMin) ==
567 !bobNewFirstNFTokenPage->isFieldPresent(sfPreviousPageMin));
574 BEAST_EXPECT(
nftCount(env, bob) == 64);
575 BEAST_EXPECT(ownerCount(env, bob) == 2);
587 if (!BEAST_EXPECT(carolMiddleNFTokenPage))
591 carolMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
593 !carolMiddleNFTokenPage->isFieldPresent(sfNextPageMin));
600 !carolLastNFTokenPage->isFieldPresent(sfPreviousPageMin));
601 BEAST_EXPECT(!carolLastNFTokenPage->isFieldPresent(sfNextPageMin));
605 env(ledgerStateFix::nftPageLinks(carol, carol), fee(linkFixFee));
613 if (!BEAST_EXPECT(carolMiddleNFTokenPage))
617 carolMiddleNFTokenPage->isFieldPresent(sfPreviousPageMin));
619 carolMiddleNFTokenPage->isFieldPresent(sfNextPageMin) &&
620 carolMiddleNFTokenPage->at(sfNextPageMin) ==
624 auto carolLastNFTokenPage = env.le(lastPageKeylet);
625 if (!BEAST_EXPECT(carolLastNFTokenPage))
629 carolLastNFTokenPage->isFieldPresent(sfPreviousPageMin) &&
630 carolLastNFTokenPage->at(sfPreviousPageMin) ==
631 carolMiddleNFTokenPageIndex);
632 BEAST_EXPECT(!carolLastNFTokenPage->isFieldPresent(sfNextPageMin));
637 carolMiddleNFTokenPage->at(sfPreviousPageMin)));
638 if (!BEAST_EXPECT(carolFirstNFTokenPage))
642 carolFirstNFTokenPage->isFieldPresent(sfNextPageMin) &&
643 carolFirstNFTokenPage->at(sfNextPageMin) ==
644 carolMiddleNFTokenPageIndex);
646 !carolFirstNFTokenPage->isFieldPresent(sfPreviousPageMin));
650 BEAST_EXPECT(
nftCount(env, carol) == 96);
651 BEAST_EXPECT(ownerCount(env, carol) == 3);
664BEAST_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