20#include <xrpld/app/tx/detail/NFTokenUtils.h>
21#include <xrpld/ledger/Dir.h>
22#include <xrpld/ledger/View.h>
23#include <xrpl/basics/algorithm.h>
24#include <xrpl/protocol/Feature.h>
25#include <xrpl/protocol/STAccount.h>
26#include <xrpl/protocol/STArray.h>
27#include <xrpl/protocol/TxFlags.h>
28#include <xrpl/protocol/nftPageMask.h>
47 view.
succ(first.key, last.key.next()).value_or(last.key)));
61 view.
succ(first.key, last.key.next()).value_or(last.key)));
80 view.
succ(first.key, last.key.next()).value_or(last.key)));
86 cp = std::make_shared<SLE>(last);
87 cp->setFieldArray(sfNFTokens, arr);
89 createCallback(view, owner);
93 STArray narr = cp->getFieldArray(sfNFTokens);
122 return (obj.getFieldH256(sfNFTokenID) & nft::pageMask) == cmp;
128 if (splitIter == narr.
end())
131 return (obj.getFieldH256(sfNFTokenID) & nft::pageMask) ==
137 if (splitIter == narr.
end())
142 if (splitIter == narr.
begin())
163 splitIter = narr.
end();
191 : carr[0].getFieldH256(sfNFTokenID);
193 auto np = std::make_shared<SLE>(
keylet::nftpage(base, tokenIDForNewPage));
195 np->key() > base.key,
196 "ripple::nft::getPageForToken : valid NFT page index");
197 np->setFieldArray(sfNFTokens, narr);
198 np->setFieldH256(sfNextPageMin, cp->key());
200 if (
auto ppm = (*cp)[~sfPreviousPageMin])
202 np->setFieldH256(sfPreviousPageMin, *ppm);
204 if (
auto p3 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *ppm)))
206 p3->setFieldH256(sfNextPageMin, np->key());
213 cp->setFieldArray(sfNFTokens, carr);
214 cp->setFieldH256(sfPreviousPageMin, np->key());
217 createCallback(view, owner);
224 return (first.key <= np->key()) ? np : cp;
226 return (first.key < np->key()) ? np : cp;
239 return lowBitsCmp < 0;
258 STArray& arr = page->peekFieldArray(sfNFTokens);
262 return (obj[sfNFTokenID] == nftokenID);
265 if (nftIter == arr.
end())
269 nftIter->setFieldVL(sfURI, *uri);
270 else if (nftIter->isFieldPresent(sfURI))
271 nftIter->makeFieldAbsent(sfURI);
282 nft.isFieldPresent(sfNFTokenID),
283 "ripple::nft::insertToken : has NFT token");
304 auto arr = page->getFieldArray(sfNFTokens);
305 arr.push_back(std::move(nft));
312 page->setFieldArray(sfNFTokens, arr);
326 if (p1->key() >= p2->key())
327 Throw<std::runtime_error>(
"mergePages: pages passed in out of order!");
329 if ((*p1)[~sfNextPageMin] != p2->key())
330 Throw<std::runtime_error>(
"mergePages: next link broken!");
332 if ((*p2)[~sfPreviousPageMin] != p1->key())
333 Throw<std::runtime_error>(
"mergePages: previous link broken!");
335 auto const p1arr = p1->getFieldArray(sfNFTokens);
336 auto const p2arr = p2->getFieldArray(sfNFTokens);
345 STArray x(p1arr.size() + p2arr.size());
354 return compareTokens(
355 a.getFieldH256(sfNFTokenID), b.getFieldH256(sfNFTokenID));
358 p2->setFieldArray(sfNFTokens, x);
364 p2->makeFieldAbsent(sfPreviousPageMin);
366 if (
auto const ppm = (*p1)[~sfPreviousPageMin])
368 auto p0 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *ppm));
371 Throw<std::runtime_error>(
"mergePages: p0 can't be located!");
373 p0->setFieldH256(sfNextPageMin, p2->key());
376 p2->setFieldH256(sfPreviousPageMin, *ppm);
395 return removeToken(view, owner, nftokenID, std::move(page));
407 auto arr = curr->getFieldArray(sfNFTokens);
411 arr.begin(), arr.end(), [&nftokenID](
STObject const& obj) {
412 return (obj[sfNFTokenID] == nftokenID);
422 auto const loadPage = [&view](
427 if (
auto const id = (*page1)[~field])
429 page2 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *
id));
432 Throw<std::runtime_error>(
433 "page " +
to_string(page1->key()) +
" has a broken " +
434 field.getName() +
" field pointing to " +
to_string(*
id));
440 auto const prev = loadPage(curr, sfPreviousPageMin);
441 auto const next = loadPage(curr, sfNextPageMin);
448 curr->setFieldArray(sfNFTokens, arr);
482 curr->peekFieldArray(sfNFTokens) = prev->peekFieldArray(sfNFTokens);
484 if (
auto const prevLink = prev->at(~sfPreviousPageMin))
486 curr->at(sfPreviousPageMin) = *prevLink;
489 auto const newPrev = loadPage(curr, sfPreviousPageMin);
490 newPrev->at(sfNextPageMin) = curr->key();
495 curr->makeFieldAbsent(sfPreviousPageMin);
512 prev->setFieldH256(sfNextPageMin, next->key());
514 prev->makeFieldAbsent(sfNextPageMin);
523 next->setFieldH256(sfPreviousPageMin, prev->key());
525 next->makeFieldAbsent(sfPreviousPageMin);
545 view.
peek(
Keylet(ltNFTOKEN_PAGE, prev->key())),
546 view.
peek(
Keylet(ltNFTOKEN_PAGE, next->key()))))
571 for (
auto const& t : page->getFieldArray(sfNFTokens))
573 if (t[sfNFTokenID] == nftokenID)
593 for (
auto const& t : page->getFieldArray(sfNFTokens))
595 if (t[sfNFTokenID] == nftokenID)
609 if (maxDeletableOffers == 0)
623 pageIndex = (*page)[~sfIndexNext];
625 auto offerIndexes = page->getFieldV256(sfIndexes);
633 for (
int i = offerIndexes.size() - 1; i >= 0; --i)
638 ++deletedOffersCount;
640 Throw<std::runtime_error>(
642 " cannot be deleted!");
645 if (maxDeletableOffers == deletedOffersCount)
648 }
while (pageIndex.value_or(0) && maxDeletableOffers != deletedOffersCount);
650 return deletedOffersCount;
662 totalOffers += iter.page_size();
672 totalOffers += iter.page_size();
683 if (offer->getType() != ltNFTOKEN_OFFER)
686 auto const owner = (*offer)[sfOwner];
690 (*offer)[sfOwnerNode],
695 auto const nftokenID = (*offer)[sfNFTokenID];
700 (*offer)[sfNFTokenOfferNode],
718 bool didRepair =
false;
725 .value_or(last.key)));
730 if (page->
key() == last.key)
734 bool const nextPresent = page->isFieldPresent(sfNextPageMin);
735 bool const prevPresent = page->isFieldPresent(sfPreviousPageMin);
736 if (nextPresent || prevPresent)
740 page->makeFieldAbsent(sfPreviousPageMin);
742 page->makeFieldAbsent(sfNextPageMin);
750 if (page->isFieldPresent(sfPreviousPageMin))
753 page->makeFieldAbsent(sfPreviousPageMin);
762 .value_or(last.key)))))
764 if (!page->isFieldPresent(sfNextPageMin) ||
765 page->getFieldH256(sfNextPageMin) != nextPage->key())
768 page->setFieldH256(sfNextPageMin, nextPage->key());
772 if (!nextPage->isFieldPresent(sfPreviousPageMin) ||
773 nextPage->getFieldH256(sfPreviousPageMin) != page->
key())
776 nextPage->setFieldH256(sfPreviousPageMin, page->
key());
780 if (nextPage->key() == last.key)
800 nextPage = std::make_shared<SLE>(last);
803 nextPage->peekFieldArray(sfNFTokens) = page->peekFieldArray(sfNFTokens);
805 if (
auto const prevLink = page->at(~sfPreviousPageMin))
807 nextPage->at(sfPreviousPageMin) = *prevLink;
810 auto const newPrev = view.
peek(
Keylet(ltNFTOKEN_PAGE, *prevLink));
812 Throw<std::runtime_error>(
813 "NFTokenPage directory for " +
to_string(owner) +
814 " cannot be repaired. Unexpected link problem.");
815 newPrev->at(sfNextPageMin) = nextPage->key();
825 "ripple::nft::repairNFTokenDirectoryLinks : next page is available");
826 if (nextPage->isFieldPresent(sfNextPageMin))
829 nextPage->makeFieldAbsent(sfNextPageMin);
862 if (!isSellOffer && !amount)
873 if (owner && owner == acctID)
885 if (!isSellOffer && !rules.
enabled(fixNFTokenNegOffer))
934 root,
"ripple::nft::tokenOfferCreatePreclaim : non-null account");
936 if (
auto minter = (*
root)[~sfNFTokenMinter]; minter != acctID)
1023 if (
auto const acct = view.
read(acctKeylet);
1049 (*sle)[sfNFTokenID] = nftokenID;
1060 auto offer = std::make_shared<SLE>(offerID);
1061 (*offer)[sfOwner] = acctID;
1062 (*offer)[sfNFTokenID] = nftokenID;
1063 (*offer)[sfAmount] = amount;
1064 (*offer)[sfFlags] = sleFlags;
1065 (*offer)[sfOwnerNode] = *ownerNode;
1066 (*offer)[sfNFTokenOfferNode] = *offerNode;
1069 (*offer)[sfExpiration] = *expiration;
1072 (*offer)[sfDestination] = *dest;
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.
const_iterator & next_page()
A class that simplifies iterating ledger directory pages.
const_iterator end() const
const_iterator begin() const
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.
Currency const & getCurrency() const
int signum() const noexcept
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 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.
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
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
static bool adjustOwnerCount(ApplyContext &ctx, int count)
@ tecNO_SUITABLE_NFTOKEN_PAGE
@ tecINSUFFICIENT_RESERVE
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
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.