20#include <xrpld/app/tx/detail/NFTokenUtils.h>
22#include <xrpl/basics/algorithm.h>
23#include <xrpl/ledger/Dir.h>
24#include <xrpl/ledger/View.h>
25#include <xrpl/protocol/Feature.h>
26#include <xrpl/protocol/STArray.h>
27#include <xrpl/protocol/TxFlags.h>
28#include <xrpl/protocol/nftPageMask.h>
48 view.
succ(first.key, last.key.next()).value_or(last.key)));
62 view.
succ(first.key, last.key.next()).value_or(last.key)));
81 view.
succ(first.key, last.key.next()).value_or(last.key)));
88 cp->setFieldArray(sfNFTokens, arr);
90 createCallback(view, owner);
94 STArray narr = cp->getFieldArray(sfNFTokens);
123 return (obj.getFieldH256(sfNFTokenID) & nft::pageMask) == cmp;
129 if (splitIter == narr.
end())
132 return (obj.getFieldH256(sfNFTokenID) & nft::pageMask) ==
138 if (splitIter == narr.
end())
143 if (splitIter == narr.
begin())
159 splitIter = narr.
end();
184 : carr[0].getFieldH256(sfNFTokenID);
188 np->key() > base.key,
189 "ripple::nft::getPageForToken : valid NFT page index");
190 np->setFieldArray(sfNFTokens, narr);
191 np->setFieldH256(sfNextPageMin, cp->key());
193 if (
auto ppm = (*cp)[~sfPreviousPageMin])
195 np->setFieldH256(sfPreviousPageMin, *ppm);
197 if (
auto p3 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *ppm)))
199 p3->setFieldH256(sfNextPageMin, np->key());
206 cp->setFieldArray(sfNFTokens, carr);
207 cp->setFieldH256(sfPreviousPageMin, np->key());
210 createCallback(view, owner);
212 return (first.key < np->key()) ? np : cp;
225 return lowBitsCmp < 0;
244 STArray& arr = page->peekFieldArray(sfNFTokens);
248 return (obj[sfNFTokenID] == nftokenID);
251 if (nftIter == arr.
end())
255 nftIter->setFieldVL(sfURI, *uri);
256 else if (nftIter->isFieldPresent(sfURI))
257 nftIter->makeFieldAbsent(sfURI);
268 nft.isFieldPresent(sfNFTokenID),
269 "ripple::nft::insertToken : has NFT token");
290 auto arr = page->getFieldArray(sfNFTokens);
291 arr.push_back(std::move(nft));
298 page->setFieldArray(sfNFTokens, arr);
312 if (p1->key() >= p2->key())
313 Throw<std::runtime_error>(
"mergePages: pages passed in out of order!");
315 if ((*p1)[~sfNextPageMin] != p2->key())
316 Throw<std::runtime_error>(
"mergePages: next link broken!");
318 if ((*p2)[~sfPreviousPageMin] != p1->key())
319 Throw<std::runtime_error>(
"mergePages: previous link broken!");
321 auto const p1arr = p1->getFieldArray(sfNFTokens);
322 auto const p2arr = p2->getFieldArray(sfNFTokens);
331 STArray x(p1arr.size() + p2arr.size());
340 return compareTokens(
341 a.getFieldH256(sfNFTokenID), b.getFieldH256(sfNFTokenID));
344 p2->setFieldArray(sfNFTokens, x);
350 p2->makeFieldAbsent(sfPreviousPageMin);
352 if (
auto const ppm = (*p1)[~sfPreviousPageMin])
354 auto p0 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *ppm));
357 Throw<std::runtime_error>(
"mergePages: p0 can't be located!");
359 p0->setFieldH256(sfNextPageMin, p2->key());
362 p2->setFieldH256(sfPreviousPageMin, *ppm);
381 return removeToken(view, owner, nftokenID, std::move(page));
393 auto arr = curr->getFieldArray(sfNFTokens);
397 arr.begin(), arr.end(), [&nftokenID](
STObject const& obj) {
398 return (obj[sfNFTokenID] == nftokenID);
408 auto const loadPage = [&view](
413 if (
auto const id = (*page1)[~field])
415 page2 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *
id));
418 Throw<std::runtime_error>(
419 "page " +
to_string(page1->key()) +
" has a broken " +
420 field.getName() +
" field pointing to " +
to_string(*
id));
426 auto const prev = loadPage(curr, sfPreviousPageMin);
427 auto const next = loadPage(curr, sfNextPageMin);
434 curr->setFieldArray(sfNFTokens, arr);
468 curr->peekFieldArray(sfNFTokens) = prev->peekFieldArray(sfNFTokens);
470 if (
auto const prevLink = prev->at(~sfPreviousPageMin))
472 curr->at(sfPreviousPageMin) = *prevLink;
475 auto const newPrev = loadPage(curr, sfPreviousPageMin);
476 newPrev->at(sfNextPageMin) = curr->key();
481 curr->makeFieldAbsent(sfPreviousPageMin);
498 prev->setFieldH256(sfNextPageMin, next->key());
500 prev->makeFieldAbsent(sfNextPageMin);
509 next->setFieldH256(sfPreviousPageMin, prev->key());
511 next->makeFieldAbsent(sfPreviousPageMin);
531 view.
peek(
Keylet(ltNFTOKEN_PAGE, prev->key())),
532 view.
peek(
Keylet(ltNFTOKEN_PAGE, next->key()))))
557 for (
auto const& t : page->getFieldArray(sfNFTokens))
559 if (t[sfNFTokenID] == nftokenID)
579 for (
auto const& t : page->getFieldArray(sfNFTokens))
581 if (t[sfNFTokenID] == nftokenID)
595 if (maxDeletableOffers == 0)
609 pageIndex = (*page)[~sfIndexNext];
611 auto offerIndexes = page->getFieldV256(sfIndexes);
619 for (
int i = offerIndexes.size() - 1; i >= 0; --i)
624 ++deletedOffersCount;
626 Throw<std::runtime_error>(
628 " cannot be deleted!");
631 if (maxDeletableOffers == deletedOffersCount)
634 }
while (pageIndex.value_or(0) && maxDeletableOffers != deletedOffersCount);
636 return deletedOffersCount;
648 totalOffers += iter.page_size();
658 totalOffers += iter.page_size();
669 if (offer->getType() != ltNFTOKEN_OFFER)
672 auto const owner = (*offer)[sfOwner];
676 (*offer)[sfOwnerNode],
681 auto const nftokenID = (*offer)[sfNFTokenID];
686 (*offer)[sfNFTokenOfferNode],
704 bool didRepair =
false;
711 .value_or(last.key)));
716 if (page->
key() == last.key)
720 bool const nextPresent = page->isFieldPresent(sfNextPageMin);
721 bool const prevPresent = page->isFieldPresent(sfPreviousPageMin);
722 if (nextPresent || prevPresent)
726 page->makeFieldAbsent(sfPreviousPageMin);
728 page->makeFieldAbsent(sfNextPageMin);
736 if (page->isFieldPresent(sfPreviousPageMin))
739 page->makeFieldAbsent(sfPreviousPageMin);
748 .value_or(last.key)))))
750 if (!page->isFieldPresent(sfNextPageMin) ||
751 page->getFieldH256(sfNextPageMin) != nextPage->key())
754 page->setFieldH256(sfNextPageMin, nextPage->key());
758 if (!nextPage->isFieldPresent(sfPreviousPageMin) ||
759 nextPage->getFieldH256(sfPreviousPageMin) != page->
key())
762 nextPage->setFieldH256(sfPreviousPageMin, page->
key());
766 if (nextPage->key() == last.key)
789 nextPage->peekFieldArray(sfNFTokens) = page->peekFieldArray(sfNFTokens);
791 if (
auto const prevLink = page->at(~sfPreviousPageMin))
793 nextPage->at(sfPreviousPageMin) = *prevLink;
796 auto const newPrev = view.
peek(
Keylet(ltNFTOKEN_PAGE, *prevLink));
798 Throw<std::runtime_error>(
799 "NFTokenPage directory for " +
to_string(owner) +
800 " cannot be repaired. Unexpected link problem.");
801 newPrev->at(sfNextPageMin) = nextPage->key();
811 "ripple::nft::repairNFTokenDirectoryLinks : next page is available");
812 if (nextPage->isFieldPresent(sfNextPageMin))
815 nextPage->makeFieldAbsent(sfNextPageMin);
848 if (!isSellOffer && !amount)
859 if (owner && owner == acctID)
863 if (dest && dest == acctID)
909 root,
"ripple::nft::tokenOfferCreatePreclaim : non-null account");
911 if (
auto minter = (*
root)[~sfNFTokenMinter]; minter != acctID)
997 if (
auto const acct = view.
read(acctKeylet);
1023 (*sle)[sfNFTokenID] = nftokenID;
1035 (*offer)[sfOwner] = acctID;
1036 (*offer)[sfNFTokenID] = nftokenID;
1037 (*offer)[sfAmount] = amount;
1038 (*offer)[sfFlags] = sleFlags;
1039 (*offer)[sfOwnerNode] = *ownerNode;
1040 (*offer)[sfNFTokenOfferNode] = *offerNode;
1043 (*offer)[sfExpiration] = *expiration;
1046 (*offer)[sfDestination] = *dest;
1067 "ripple::nft::checkTrustlineAuthorized : valid to check.");
1069 if (view.
rules().
enabled(fixEnforceNFTokenTrustlineV2))
1074 JLOG(j.
debug()) <<
"ripple::nft::checkTrustlineAuthorized: can't "
1075 "receive IOUs from non-existent issuer: "
1091 auto const trustLine =
1102 if (!trustLine->isFlag(
1123 "ripple::nft::checkTrustlineDeepFrozen : valid to check.");
1130 JLOG(j.
debug()) <<
"ripple::nft::checkTrustlineDeepFrozen: can't "
1131 "receive IOUs from non-existent issuer: "
1145 auto const trustLine =
1155 bool const deepFrozen =
T back_inserter(T... args)
A generic endpoint for log messages.
Writeable view to a ledger, for applying a transaction.
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
constexpr TIss const & get() const
const_iterator & next_page()
A class that simplifies iterating ledger directory pages.
const_iterator end() const
const_iterator begin() const
A currency issued by an account.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual std::optional< key_type > succ(key_type const &key, std::optional< key_type > const &last=std::nullopt) const =0
Return the key of the next state item.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual Rules const & rules() const =0
Returns the tx processing rules.
Rules controlling protocol behavior.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Asset const & asset() const
Currency const & getCurrency() const
bool negative() const noexcept
AccountID const & getIssuer() const
Issue const & issue() const
bool native() const noexcept
iterator erase(iterator pos)
uint256 getFieldH256(SField const &field) const
A type that represents either a sequence value or a ticket value.
constexpr std::uint32_t value() const
T make_move_iterator(T... args)
Keylet line(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Keylet nftpage(Keylet const &k, uint256 const &token)
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
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 ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet nft_buys(uint256 const &id) noexcept
The directory of buy offers for the specified NFT.
Keylet nft_sells(uint256 const &id) noexcept
The directory of sell offers for the specified NFT.
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
static std::shared_ptr< SLE > getPageForToken(ApplyView &view, AccountID const &owner, uint256 const &id, std::function< void(ApplyView &, AccountID const &)> const &createCallback)
static std::shared_ptr< SLE const > locatePage(ReadView const &view, AccountID const &owner, uint256 const &id)
TER removeToken(ApplyView &view, AccountID const &owner, uint256 const &nftokenID)
Remove the token from the owner's token directory.
NotTEC tokenOfferCreatePreflight(AccountID const &acctID, STAmount const &amount, std::optional< AccountID > const &dest, std::optional< std::uint32_t > const &expiration, std::uint16_t nftFlags, Rules const &rules, std::optional< AccountID > const &owner, std::uint32_t txFlags)
Preflight checks shared by NFTokenCreateOffer and NFTokenMint.
TER tokenOfferCreateApply(ApplyView &view, AccountID const &acctID, STAmount const &amount, std::optional< AccountID > const &dest, std::optional< std::uint32_t > const &expiration, SeqProxy seqProxy, uint256 const &nftokenID, XRPAmount const &priorBalance, beast::Journal j, std::uint32_t txFlags)
doApply implementation shared by NFTokenCreateOffer and NFTokenMint
constexpr std::uint16_t const flagOnlyXRP
TER checkTrustlineDeepFrozen(ReadView const &view, AccountID const id, beast::Journal const j, Issue const &issue)
TER tokenOfferCreatePreclaim(ReadView const &view, AccountID const &acctID, AccountID const &nftIssuer, STAmount const &amount, std::optional< AccountID > const &dest, std::uint16_t nftFlags, std::uint16_t xferFee, beast::Journal j, std::optional< AccountID > const &owner, std::uint32_t txFlags)
Preclaim checks shared by NFTokenCreateOffer and NFTokenMint.
bool repairNFTokenDirectoryLinks(ApplyView &view, AccountID const &owner)
Repairs the links in an NFTokenPage directory.
bool deleteTokenOffer(ApplyView &view, std::shared_ptr< SLE > const &offer)
Deletes the given token offer.
std::optional< TokenAndPage > findTokenAndPage(ApplyView &view, AccountID const &owner, uint256 const &nftokenID)
TER insertToken(ApplyView &view, AccountID owner, STObject &&nft)
Insert the token in the owner's token directory.
TER notTooManyOffers(ReadView const &view, uint256 const &nftokenID)
Returns tesSUCCESS if NFToken has few enough offers that it can be burned.
std::optional< STObject > findToken(ReadView const &view, AccountID const &owner, uint256 const &nftokenID)
Finds the specified token in the owner's token directory.
bool compareTokens(uint256 const &a, uint256 const &b)
constexpr std::uint16_t const flagTransferable
constexpr std::uint16_t const flagCreateTrustLines
TER changeTokenURI(ApplyView &view, AccountID const &owner, uint256 const &nftokenID, std::optional< ripple::Slice > const &uri)
std::size_t removeTokenOffersWithLimit(ApplyView &view, Keylet const &directory, std::size_t maxDeletableOffers)
Delete up to a specified number of offers from the specified token offer directory.
TER checkTrustlineAuthorized(ReadView const &view, AccountID const id, beast::Journal const j, Issue const &issue)
static bool mergePages(ApplyView &view, std::shared_ptr< SLE > const &p1, std::shared_ptr< SLE > const &p2)
uint256 constexpr pageMask(std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff"))
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
bool isXRP(AccountID const &c)
constexpr std::uint32_t const tfSellNFToken
@ lsfDisallowIncomingNFTokenOffer
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
std::size_t constexpr maxDeletableTokenOfferEntries
The maximum number of offers in an offer directory for NFT to be burnable.
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
std::size_t constexpr dirMaxTokensPerPage
The maximum number of items in an NFT page.
bool isFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
@ tefNFTOKEN_IS_NOT_TRANSFERABLE
@ tecNO_SUITABLE_NFTOKEN_PAGE
@ tecINSUFFICIENT_RESERVE
std::string to_string(base_uint< Bits, Tag > const &a)
Number root(Number f, unsigned d)
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
A pair of SHAMap key and LedgerEntryType.
A field with a type known at compile time.