1#include <xrpld/app/tx/detail/NFTokenUtils.h> 
    3#include <xrpl/basics/algorithm.h> 
    4#include <xrpl/ledger/Dir.h> 
    5#include <xrpl/ledger/View.h> 
    6#include <xrpl/protocol/Feature.h> 
    7#include <xrpl/protocol/STArray.h> 
    8#include <xrpl/protocol/TxFlags.h> 
    9#include <xrpl/protocol/nftPageMask.h> 
   29        view.
succ(first.key, last.key.next()).value_or(last.key)));
 
 
   43        view.
succ(first.key, last.key.next()).value_or(last.key)));
 
 
   62        view.
succ(first.key, last.key.next()).value_or(last.key)));
 
   69        cp->setFieldArray(sfNFTokens, arr);
 
   71        createCallback(view, owner);
 
   75    STArray narr = cp->getFieldArray(sfNFTokens);
 
  104                return (obj.getFieldH256(sfNFTokenID) & nft::pageMask) == cmp;
 
  110        if (splitIter == narr.
end())
 
  113                    return (obj.getFieldH256(sfNFTokenID) & nft::pageMask) ==
 
  119        if (splitIter == narr.
end())
 
  124        if (splitIter == narr.
begin())
 
  140                splitIter = narr.
end();
 
  165        : carr[0].getFieldH256(sfNFTokenID);
 
  169        np->key() > base.key,
 
  170        "ripple::nft::getPageForToken : valid NFT page index");
 
  171    np->setFieldArray(sfNFTokens, narr);
 
  172    np->setFieldH256(sfNextPageMin, cp->key());
 
  174    if (
auto ppm = (*cp)[~sfPreviousPageMin])
 
  176        np->setFieldH256(sfPreviousPageMin, *ppm);
 
  178        if (
auto p3 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *ppm)))
 
  180            p3->setFieldH256(sfNextPageMin, np->key());
 
  187    cp->setFieldArray(sfNFTokens, carr);
 
  188    cp->setFieldH256(sfPreviousPageMin, np->key());
 
  191    createCallback(view, owner);
 
  193    return (first.key < np->key()) ? np : cp;
 
 
  206        return lowBitsCmp < 0;
 
 
  225    STArray& arr = page->peekFieldArray(sfNFTokens);
 
  229            return (obj[sfNFTokenID] == nftokenID);
 
  232    if (nftIter == arr.
end())
 
  236        nftIter->setFieldVL(sfURI, *uri);
 
  237    else if (nftIter->isFieldPresent(sfURI))
 
  238        nftIter->makeFieldAbsent(sfURI);
 
 
  249        nft.isFieldPresent(sfNFTokenID),
 
  250        "ripple::nft::insertToken : has NFT token");
 
  271        auto arr = page->getFieldArray(sfNFTokens);
 
  272        arr.push_back(std::move(nft));
 
  279        page->setFieldArray(sfNFTokens, arr);
 
 
  293    if (p1->key() >= p2->key())
 
  294        Throw<std::runtime_error>(
"mergePages: pages passed in out of order!");
 
  296    if ((*p1)[~sfNextPageMin] != p2->key())
 
  297        Throw<std::runtime_error>(
"mergePages: next link broken!");
 
  299    if ((*p2)[~sfPreviousPageMin] != p1->key())
 
  300        Throw<std::runtime_error>(
"mergePages: previous link broken!");
 
  302    auto const p1arr = p1->getFieldArray(sfNFTokens);
 
  303    auto const p2arr = p2->getFieldArray(sfNFTokens);
 
  312    STArray x(p1arr.size() + p2arr.size());
 
  321            return compareTokens(
 
  322                a.getFieldH256(sfNFTokenID), b.getFieldH256(sfNFTokenID));
 
  325    p2->setFieldArray(sfNFTokens, x);
 
  331    p2->makeFieldAbsent(sfPreviousPageMin);
 
  333    if (
auto const ppm = (*p1)[~sfPreviousPageMin])
 
  335        auto p0 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *ppm));
 
  338            Throw<std::runtime_error>(
"mergePages: p0 can't be located!");
 
  340        p0->setFieldH256(sfNextPageMin, p2->key());
 
  343        p2->setFieldH256(sfPreviousPageMin, *ppm);
 
 
  362    return removeToken(view, owner, nftokenID, std::move(page));
 
 
  374    auto arr = curr->getFieldArray(sfNFTokens);
 
  378            arr.begin(), arr.end(), [&nftokenID](
STObject const& obj) {
 
  379                return (obj[sfNFTokenID] == nftokenID);
 
  389    auto const loadPage = [&view](
 
  394        if (
auto const id = (*page1)[~field])
 
  396            page2 = view.
peek(
Keylet(ltNFTOKEN_PAGE, *
id));
 
  399                Throw<std::runtime_error>(
 
  400                    "page " + 
to_string(page1->key()) + 
" has a broken " +
 
  401                    field.getName() + 
" field pointing to " + 
to_string(*
id));
 
  407    auto const prev = loadPage(curr, sfPreviousPageMin);
 
  408    auto const next = loadPage(curr, sfNextPageMin);
 
  415        curr->setFieldArray(sfNFTokens, arr);
 
  449            curr->peekFieldArray(sfNFTokens) = prev->peekFieldArray(sfNFTokens);
 
  451            if (
auto const prevLink = prev->at(~sfPreviousPageMin))
 
  453                curr->at(sfPreviousPageMin) = *prevLink;
 
  456                auto const newPrev = loadPage(curr, sfPreviousPageMin);
 
  457                newPrev->at(sfNextPageMin) = curr->key();
 
  462                curr->makeFieldAbsent(sfPreviousPageMin);
 
  479            prev->setFieldH256(sfNextPageMin, next->key());
 
  481            prev->makeFieldAbsent(sfNextPageMin);
 
  490            next->setFieldH256(sfPreviousPageMin, prev->key());
 
  492            next->makeFieldAbsent(sfPreviousPageMin);
 
  512            view.
peek(
Keylet(ltNFTOKEN_PAGE, prev->key())),
 
  513            view.
peek(
Keylet(ltNFTOKEN_PAGE, next->key()))))
 
 
  538    for (
auto const& t : page->getFieldArray(sfNFTokens))
 
  540        if (t[sfNFTokenID] == nftokenID)
 
 
  560    for (
auto const& t : page->getFieldArray(sfNFTokens))
 
  562        if (t[sfNFTokenID] == nftokenID)
 
 
  576    if (maxDeletableOffers == 0)
 
  590        pageIndex = (*page)[~sfIndexNext];
 
  592        auto offerIndexes = page->getFieldV256(sfIndexes);
 
  600        for (
int i = offerIndexes.size() - 1; i >= 0; --i)
 
  605                    ++deletedOffersCount;
 
  607                    Throw<std::runtime_error>(
 
  609                        " cannot be deleted!");
 
  612            if (maxDeletableOffers == deletedOffersCount)
 
  615    } 
while (pageIndex.value_or(0) && maxDeletableOffers != deletedOffersCount);
 
  617    return deletedOffersCount;
 
 
  629            totalOffers += iter.page_size();
 
  639            totalOffers += iter.page_size();
 
 
  650    if (offer->getType() != ltNFTOKEN_OFFER)
 
  653    auto const owner = (*offer)[sfOwner];
 
  657            (*offer)[sfOwnerNode],
 
  662    auto const nftokenID = (*offer)[sfNFTokenID];
 
  667            (*offer)[sfNFTokenOfferNode],
 
 
  685    bool didRepair = 
false;
 
  692            .value_or(last.key)));
 
  697    if (page->
key() == last.key)
 
  701        bool const nextPresent = page->isFieldPresent(sfNextPageMin);
 
  702        bool const prevPresent = page->isFieldPresent(sfPreviousPageMin);
 
  703        if (nextPresent || prevPresent)
 
  707                page->makeFieldAbsent(sfPreviousPageMin);
 
  709                page->makeFieldAbsent(sfNextPageMin);
 
  717    if (page->isFieldPresent(sfPreviousPageMin))
 
  720        page->makeFieldAbsent(sfPreviousPageMin);
 
  729                 .value_or(last.key)))))
 
  731        if (!page->isFieldPresent(sfNextPageMin) ||
 
  732            page->getFieldH256(sfNextPageMin) != nextPage->key())
 
  735            page->setFieldH256(sfNextPageMin, nextPage->key());
 
  739        if (!nextPage->isFieldPresent(sfPreviousPageMin) ||
 
  740            nextPage->getFieldH256(sfPreviousPageMin) != page->
key())
 
  743            nextPage->setFieldH256(sfPreviousPageMin, page->
key());
 
  747        if (nextPage->key() == last.key)
 
  770        nextPage->peekFieldArray(sfNFTokens) = page->peekFieldArray(sfNFTokens);
 
  772        if (
auto const prevLink = page->at(~sfPreviousPageMin))
 
  774            nextPage->at(sfPreviousPageMin) = *prevLink;
 
  777            auto const newPrev = view.
peek(
Keylet(ltNFTOKEN_PAGE, *prevLink));
 
  779                Throw<std::runtime_error>(
 
  780                    "NFTokenPage directory for " + 
to_string(owner) +
 
  781                    " cannot be repaired.  Unexpected link problem.");
 
  782            newPrev->at(sfNextPageMin) = nextPage->key();
 
  792        "ripple::nft::repairNFTokenDirectoryLinks : next page is available");
 
  793    if (nextPage->isFieldPresent(sfNextPageMin))
 
  796        nextPage->makeFieldAbsent(sfNextPageMin);
 
 
  829    if (!isSellOffer && !amount)
 
  840    if (owner && owner == acctID)
 
  844    if (dest && dest == acctID)
 
 
  890            root, 
"ripple::nft::tokenOfferCreatePreclaim : non-null account");
 
  892        if (
auto minter = (*
root)[~sfNFTokenMinter]; minter != acctID)
 
 
  978    if (
auto const acct = view.
read(acctKeylet);
 
 1004                (*sle)[sfNFTokenID] = nftokenID;
 
 1016        (*offer)[sfOwner] = acctID;
 
 1017        (*offer)[sfNFTokenID] = nftokenID;
 
 1018        (*offer)[sfAmount] = amount;
 
 1019        (*offer)[sfFlags] = sleFlags;
 
 1020        (*offer)[sfOwnerNode] = *ownerNode;
 
 1021        (*offer)[sfNFTokenOfferNode] = *offerNode;
 
 1024            (*offer)[sfExpiration] = *expiration;
 
 1027            (*offer)[sfDestination] = *dest;
 
 
 1048        "ripple::nft::checkTrustlineAuthorized : valid to check.");
 
 1050    if (view.
rules().
enabled(fixEnforceNFTokenTrustlineV2))
 
 1055            JLOG(j.
debug()) << 
"ripple::nft::checkTrustlineAuthorized: can't " 
 1056                               "receive IOUs from non-existent issuer: " 
 1072            auto const trustLine =
 
 1083            if (!trustLine->isFlag(
 
 
 1104        "ripple::nft::checkTrustlineDeepFrozen : valid to check.");
 
 1111            JLOG(j.
debug()) << 
"ripple::nft::checkTrustlineDeepFrozen: can't " 
 1112                               "receive IOUs from non-existent issuer: " 
 1126        auto const trustLine =
 
 1136        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.