20#include <xrpld/app/tx/detail/NFTokenUtils.h>
21#include <xrpld/ledger/Dir.h>
22#include <xrpld/ledger/View.h>
24#include <xrpl/basics/algorithm.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())
164 splitIter = narr.
end();
192 : carr[0].getFieldH256(sfNFTokenID);
196 np->key() > base.key,
197 "ripple::nft::getPageForToken : valid NFT page index");
198 np->setFieldArray(sfNFTokens, narr);
199 np->setFieldH256(sfNextPageMin, cp->key());
201 if (
auto ppm = (*cp)[~sfPreviousPageMin])
203 np->setFieldH256(sfPreviousPageMin, *ppm);
205 if (
auto p3 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *ppm)))
207 p3->setFieldH256(sfNextPageMin, np->key());
214 cp->setFieldArray(sfNFTokens, carr);
215 cp->setFieldH256(sfPreviousPageMin, np->key());
218 createCallback(view, owner);
225 return (first.key <= np->key()) ? np : cp;
227 return (first.key < np->key()) ? np : cp;
240 return lowBitsCmp < 0;
259 STArray& arr = page->peekFieldArray(sfNFTokens);
263 return (obj[sfNFTokenID] == nftokenID);
266 if (nftIter == arr.
end())
270 nftIter->setFieldVL(sfURI, *uri);
271 else if (nftIter->isFieldPresent(sfURI))
272 nftIter->makeFieldAbsent(sfURI);
283 nft.isFieldPresent(sfNFTokenID),
284 "ripple::nft::insertToken : has NFT token");
305 auto arr = page->getFieldArray(sfNFTokens);
306 arr.push_back(std::move(nft));
313 page->setFieldArray(sfNFTokens, arr);
327 if (p1->key() >= p2->key())
328 Throw<std::runtime_error>(
"mergePages: pages passed in out of order!");
330 if ((*p1)[~sfNextPageMin] != p2->key())
331 Throw<std::runtime_error>(
"mergePages: next link broken!");
333 if ((*p2)[~sfPreviousPageMin] != p1->key())
334 Throw<std::runtime_error>(
"mergePages: previous link broken!");
336 auto const p1arr = p1->getFieldArray(sfNFTokens);
337 auto const p2arr = p2->getFieldArray(sfNFTokens);
346 STArray x(p1arr.size() + p2arr.size());
355 return compareTokens(
356 a.getFieldH256(sfNFTokenID), b.getFieldH256(sfNFTokenID));
359 p2->setFieldArray(sfNFTokens, x);
365 p2->makeFieldAbsent(sfPreviousPageMin);
367 if (
auto const ppm = (*p1)[~sfPreviousPageMin])
369 auto p0 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *ppm));
372 Throw<std::runtime_error>(
"mergePages: p0 can't be located!");
374 p0->setFieldH256(sfNextPageMin, p2->key());
377 p2->setFieldH256(sfPreviousPageMin, *ppm);
396 return removeToken(view, owner, nftokenID, std::move(page));
408 auto arr = curr->getFieldArray(sfNFTokens);
412 arr.begin(), arr.end(), [&nftokenID](
STObject const& obj) {
413 return (obj[sfNFTokenID] == nftokenID);
423 auto const loadPage = [&view](
428 if (
auto const id = (*page1)[~field])
430 page2 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *
id));
433 Throw<std::runtime_error>(
434 "page " +
to_string(page1->key()) +
" has a broken " +
435 field.getName() +
" field pointing to " +
to_string(*
id));
441 auto const prev = loadPage(curr, sfPreviousPageMin);
442 auto const next = loadPage(curr, sfNextPageMin);
449 curr->setFieldArray(sfNFTokens, arr);
483 curr->peekFieldArray(sfNFTokens) = prev->peekFieldArray(sfNFTokens);
485 if (
auto const prevLink = prev->at(~sfPreviousPageMin))
487 curr->at(sfPreviousPageMin) = *prevLink;
490 auto const newPrev = loadPage(curr, sfPreviousPageMin);
491 newPrev->at(sfNextPageMin) = curr->key();
496 curr->makeFieldAbsent(sfPreviousPageMin);
513 prev->setFieldH256(sfNextPageMin, next->key());
515 prev->makeFieldAbsent(sfNextPageMin);
524 next->setFieldH256(sfPreviousPageMin, prev->key());
526 next->makeFieldAbsent(sfPreviousPageMin);
546 view.
peek(
Keylet(ltNFTOKEN_PAGE, prev->key())),
547 view.
peek(
Keylet(ltNFTOKEN_PAGE, next->key()))))
572 for (
auto const& t : page->getFieldArray(sfNFTokens))
574 if (t[sfNFTokenID] == nftokenID)
594 for (
auto const& t : page->getFieldArray(sfNFTokens))
596 if (t[sfNFTokenID] == nftokenID)
610 if (maxDeletableOffers == 0)
624 pageIndex = (*page)[~sfIndexNext];
626 auto offerIndexes = page->getFieldV256(sfIndexes);
634 for (
int i = offerIndexes.size() - 1; i >= 0; --i)
639 ++deletedOffersCount;
641 Throw<std::runtime_error>(
643 " cannot be deleted!");
646 if (maxDeletableOffers == deletedOffersCount)
649 }
while (pageIndex.value_or(0) && maxDeletableOffers != deletedOffersCount);
651 return deletedOffersCount;
663 totalOffers += iter.page_size();
673 totalOffers += iter.page_size();
684 if (offer->getType() != ltNFTOKEN_OFFER)
687 auto const owner = (*offer)[sfOwner];
691 (*offer)[sfOwnerNode],
696 auto const nftokenID = (*offer)[sfNFTokenID];
701 (*offer)[sfNFTokenOfferNode],
719 bool didRepair =
false;
726 .value_or(last.key)));
731 if (page->
key() == last.key)
735 bool const nextPresent = page->isFieldPresent(sfNextPageMin);
736 bool const prevPresent = page->isFieldPresent(sfPreviousPageMin);
737 if (nextPresent || prevPresent)
741 page->makeFieldAbsent(sfPreviousPageMin);
743 page->makeFieldAbsent(sfNextPageMin);
751 if (page->isFieldPresent(sfPreviousPageMin))
754 page->makeFieldAbsent(sfPreviousPageMin);
763 .value_or(last.key)))))
765 if (!page->isFieldPresent(sfNextPageMin) ||
766 page->getFieldH256(sfNextPageMin) != nextPage->key())
769 page->setFieldH256(sfNextPageMin, nextPage->key());
773 if (!nextPage->isFieldPresent(sfPreviousPageMin) ||
774 nextPage->getFieldH256(sfPreviousPageMin) != page->
key())
777 nextPage->setFieldH256(sfPreviousPageMin, page->
key());
781 if (nextPage->key() == last.key)
804 nextPage->peekFieldArray(sfNFTokens) = page->peekFieldArray(sfNFTokens);
806 if (
auto const prevLink = page->at(~sfPreviousPageMin))
808 nextPage->at(sfPreviousPageMin) = *prevLink;
811 auto const newPrev = view.
peek(
Keylet(ltNFTOKEN_PAGE, *prevLink));
813 Throw<std::runtime_error>(
814 "NFTokenPage directory for " +
to_string(owner) +
815 " cannot be repaired. Unexpected link problem.");
816 newPrev->at(sfNextPageMin) = nextPage->key();
826 "ripple::nft::repairNFTokenDirectoryLinks : next page is available");
827 if (nextPage->isFieldPresent(sfNextPageMin))
830 nextPage->makeFieldAbsent(sfNextPageMin);
863 if (!isSellOffer && !amount)
874 if (owner && owner == acctID)
886 if (!isSellOffer && !rules.
enabled(fixNFTokenNegOffer))
935 root,
"ripple::nft::tokenOfferCreatePreclaim : non-null account");
937 if (
auto minter = (*
root)[~sfNFTokenMinter]; minter != acctID)
1036 if (
auto const acct = view.
read(acctKeylet);
1062 (*sle)[sfNFTokenID] = nftokenID;
1074 (*offer)[sfOwner] = acctID;
1075 (*offer)[sfNFTokenID] = nftokenID;
1076 (*offer)[sfAmount] = amount;
1077 (*offer)[sfFlags] = sleFlags;
1078 (*offer)[sfOwnerNode] = *ownerNode;
1079 (*offer)[sfNFTokenOfferNode] = *offerNode;
1082 (*offer)[sfExpiration] = *expiration;
1085 (*offer)[sfDestination] = *dest;
1106 "ripple::nft::checkTrustlineAuthorized : valid to check.");
1108 if (view.
rules().
enabled(fixEnforceNFTokenTrustlineV2))
1113 JLOG(j.
debug()) <<
"ripple::nft::checkTrustlineAuthorized: can't "
1114 "receive IOUs from non-existent issuer: "
1130 auto const trustLine =
1141 if (!trustLine->isFlag(
1162 "ripple::nft::checkTrustlineDeepFrozen : valid to check.");
1169 JLOG(j.
debug()) <<
"ripple::nft::checkTrustlineDeepFrozen: can't "
1170 "receive IOUs from non-existent issuer: "
1184 auto const trustLine =
1194 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
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 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
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.