mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-24 20:47:12 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57da6709a0 | ||
|
|
d6e8b9d273 | ||
|
|
3571bfe25f | ||
|
|
13d8989c35 | ||
|
|
ff3e8fe2d3 | ||
|
|
4a0a10747d | ||
|
|
48d9881906 | ||
|
|
96a0c607b1 | ||
|
|
72eaa4c0c6 |
No files matched your search
@@ -8,6 +8,11 @@
|
|||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
#include <xrpl/protocol/Keylet.h>
|
#include <xrpl/protocol/Keylet.h>
|
||||||
#include <xrpl/protocol/LedgerFormats.h>
|
#include <xrpl/protocol/LedgerFormats.h>
|
||||||
|
#include <xrpl/protocol/SField.h>
|
||||||
|
#include <xrpl/protocol/STArray.h> // IWYU pragma: keep
|
||||||
|
#include <xrpl/protocol/STObject.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
@@ -29,6 +34,37 @@ public:
|
|||||||
: Base(keylet::nftokenPage(page, token), view, j)
|
: Base(keylet::nftokenPage(page, token), view, j)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the token with ID @p id if this page holds it.
|
||||||
|
*
|
||||||
|
* @param id the ID of the token to look for.
|
||||||
|
* @return the token, or std::nullopt if this page does not hold it.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::optional<STObject>
|
||||||
|
findToken(UInt256 const& id) const
|
||||||
|
{
|
||||||
|
for (auto const& t : (*this)->getFieldArray(sfNFTokens))
|
||||||
|
{
|
||||||
|
if (t[sfNFTokenID] == id)
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds @p nft to this page's NFTokens array, keeps the array sorted, and
|
||||||
|
* updates the page.
|
||||||
|
*
|
||||||
|
* The caller must make sure that the token belongs on this page and that
|
||||||
|
* the page has space for it.
|
||||||
|
*
|
||||||
|
* @param nft the token to add.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
insertToken(STObject&& nft)
|
||||||
|
requires Base::kIsWritable;
|
||||||
};
|
};
|
||||||
|
|
||||||
using NFTokenPageEntryR = NFTokenPageEntry<ReadView>;
|
using NFTokenPageEntryR = NFTokenPageEntry<ReadView>;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <xrpl/beast/utility/Journal.h>
|
#include <xrpl/beast/utility/Journal.h>
|
||||||
#include <xrpl/ledger/ApplyView.h>
|
#include <xrpl/ledger/ApplyView.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
#include <xrpl/ledger/entries/NFTokenPageEntry.h>
|
||||||
#include <xrpl/protocol/AccountID.h>
|
#include <xrpl/protocol/AccountID.h>
|
||||||
#include <xrpl/protocol/Issue.h>
|
#include <xrpl/protocol/Issue.h>
|
||||||
#include <xrpl/protocol/Keylet.h>
|
#include <xrpl/protocol/Keylet.h>
|
||||||
@@ -46,9 +47,10 @@ findToken(ReadView const& view, AccountID const& owner, UInt256 const& nftokenID
|
|||||||
struct TokenAndPage
|
struct TokenAndPage
|
||||||
{
|
{
|
||||||
STObject token;
|
STObject token;
|
||||||
SLE::pointer page;
|
NFTokenPageEntryW page;
|
||||||
|
|
||||||
TokenAndPage(STObject token, SLE::pointer page) : token(std::move(token)), page(std::move(page))
|
TokenAndPage(STObject token, NFTokenPageEntryW page)
|
||||||
|
: token(std::move(token)), page(std::move(page))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -68,7 +70,11 @@ TER
|
|||||||
removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID);
|
removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID);
|
||||||
|
|
||||||
TER
|
TER
|
||||||
removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, SLE::Ref page);
|
removeToken(
|
||||||
|
ApplyView& view,
|
||||||
|
AccountID const& owner,
|
||||||
|
UInt256 const& nftokenID,
|
||||||
|
NFTokenPageEntryW& page);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the given token offer.
|
* Deletes the given token offer.
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#include <xrpl/ledger/entries/NFTokenPageEntry.h>
|
||||||
|
|
||||||
|
#include <xrpl/ledger/ApplyView.h>
|
||||||
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
|
||||||
|
#include <xrpl/protocol/SField.h>
|
||||||
|
#include <xrpl/protocol/STObject.h>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace xrpl {
|
||||||
|
|
||||||
|
template <typename ViewT>
|
||||||
|
void
|
||||||
|
NFTokenPageEntry<ViewT>::insertToken(STObject&& nft)
|
||||||
|
requires Base::kIsWritable
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto arr = (*this)->getFieldArray(sfNFTokens);
|
||||||
|
arr.pushBack(std::move(nft));
|
||||||
|
|
||||||
|
arr.sort([](STObject const& o1, STObject const& o2) {
|
||||||
|
return nft::compareTokens(o1.getFieldH256(sfNFTokenID), o2.getFieldH256(sfNFTokenID));
|
||||||
|
});
|
||||||
|
|
||||||
|
(*this)->setFieldArray(sfNFTokens, arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
template class NFTokenPageEntry<ReadView>;
|
||||||
|
template class NFTokenPageEntry<ApplyView>;
|
||||||
|
|
||||||
|
} // namespace xrpl
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/ledger/ApplyView.h>
|
#include <xrpl/ledger/ApplyView.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
#include <xrpl/ledger/entries/NFTokenPageEntry.h>
|
||||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
|
|
||||||
namespace xrpl::nft {
|
namespace xrpl::nft {
|
||||||
|
|
||||||
static SLE::const_pointer
|
static NFTokenPageEntryR
|
||||||
locatePage(ReadView const& view, AccountID const& owner, UInt256 const& id)
|
locatePage(ReadView const& view, AccountID const& owner, UInt256 const& id)
|
||||||
{
|
{
|
||||||
auto const first = keylet::nftokenPage(keylet::nftokenPageMin(owner), id);
|
auto const first = keylet::nftokenPage(keylet::nftokenPageMin(owner), id);
|
||||||
@@ -52,11 +53,11 @@ locatePage(ReadView const& view, AccountID const& owner, UInt256 const& id)
|
|||||||
// This NFT can only be found in the first page with a key that's strictly
|
// This NFT can only be found in the first page with a key that's strictly
|
||||||
// greater than `first`, so look for that, up until the maximum possible
|
// greater than `first`, so look for that, up until the maximum possible
|
||||||
// page.
|
// page.
|
||||||
return view.read(
|
return NFTokenPageEntryR(
|
||||||
Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key)));
|
Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key)), view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SLE::pointer
|
static NFTokenPageEntryW
|
||||||
locatePage(ApplyView& view, AccountID const& owner, UInt256 const& id)
|
locatePage(ApplyView& view, AccountID const& owner, UInt256 const& id)
|
||||||
{
|
{
|
||||||
auto const first = keylet::nftokenPage(keylet::nftokenPageMin(owner), id);
|
auto const first = keylet::nftokenPage(keylet::nftokenPageMin(owner), id);
|
||||||
@@ -65,11 +66,11 @@ locatePage(ApplyView& view, AccountID const& owner, UInt256 const& id)
|
|||||||
// This NFT can only be found in the first page with a key that's strictly
|
// This NFT can only be found in the first page with a key that's strictly
|
||||||
// greater than `first`, so look for that, up until the maximum possible
|
// greater than `first`, so look for that, up until the maximum possible
|
||||||
// page.
|
// page.
|
||||||
return view.peek(
|
return NFTokenPageEntryW(
|
||||||
Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key)));
|
Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key)), view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SLE::pointer
|
static std::optional<NFTokenPageEntryW>
|
||||||
getPageForToken(
|
getPageForToken(
|
||||||
ApplyView& view,
|
ApplyView& view,
|
||||||
AccountID const& owner,
|
AccountID const& owner,
|
||||||
@@ -83,18 +84,19 @@ getPageForToken(
|
|||||||
// This NFT can only be found in the first page with a key that's strictly
|
// This NFT can only be found in the first page with a key that's strictly
|
||||||
// greater than `first`, so look for that, up until the maximum possible
|
// greater than `first`, so look for that, up until the maximum possible
|
||||||
// page.
|
// page.
|
||||||
auto cp =
|
NFTokenPageEntryW cp(
|
||||||
view.peek(Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key)));
|
Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key)), view);
|
||||||
|
|
||||||
// A suitable page doesn't exist; we'll have to create one.
|
// A suitable page doesn't exist; we'll have to create one.
|
||||||
if (!cp)
|
if (!cp)
|
||||||
{
|
{
|
||||||
STArray const arr;
|
STArray const arr;
|
||||||
cp = std::make_shared<SLE>(last);
|
NFTokenPageEntryW newPage(last, view);
|
||||||
cp->setFieldArray(sfNFTokens, arr);
|
newPage.newSLE();
|
||||||
view.insert(cp);
|
newPage->setFieldArray(sfNFTokens, arr);
|
||||||
|
newPage.insert();
|
||||||
createCallback(view, owner);
|
createCallback(view, owner);
|
||||||
return cp;
|
return newPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
STArray narr = cp->getFieldArray(sfNFTokens);
|
STArray narr = cp->getFieldArray(sfNFTokens);
|
||||||
@@ -139,7 +141,7 @@ getPageForToken(
|
|||||||
// There should be no circumstance when splitIter == end(), but if it
|
// There should be no circumstance when splitIter == end(), but if it
|
||||||
// were to happen we should bail out because something is confused.
|
// were to happen we should bail out because something is confused.
|
||||||
if (splitIter == narr.end())
|
if (splitIter == narr.end())
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
|
|
||||||
// If splitIter == begin(), then the entire page is filled with
|
// If splitIter == begin(), then the entire page is filled with
|
||||||
// equivalent tokens. This requires special handling.
|
// equivalent tokens. This requires special handling.
|
||||||
@@ -150,7 +152,7 @@ getPageForToken(
|
|||||||
{
|
{
|
||||||
// If the passed in id belongs exactly on this (full) page
|
// If the passed in id belongs exactly on this (full) page
|
||||||
// this account simply cannot store the NFT.
|
// this account simply cannot store the NFT.
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relation > 0)
|
if (relation > 0)
|
||||||
@@ -184,7 +186,8 @@ getPageForToken(
|
|||||||
? narr[kDirMaxTokensPerPage - 1].getFieldH256(sfNFTokenID).next()
|
? narr[kDirMaxTokensPerPage - 1].getFieldH256(sfNFTokenID).next()
|
||||||
: carr[0].getFieldH256(sfNFTokenID);
|
: carr[0].getFieldH256(sfNFTokenID);
|
||||||
|
|
||||||
auto np = std::make_shared<SLE>(keylet::nftokenPage(base, tokenIDForNewPage));
|
NFTokenPageEntryW np(base, tokenIDForNewPage, view);
|
||||||
|
np.newSLE();
|
||||||
XRPL_ASSERT(np->key() > base.key, "xrpl::nft::getPageForToken : valid NFT page index");
|
XRPL_ASSERT(np->key() > base.key, "xrpl::nft::getPageForToken : valid NFT page index");
|
||||||
np->setFieldArray(sfNFTokens, narr);
|
np->setFieldArray(sfNFTokens, narr);
|
||||||
np->setFieldH256(sfNextPageMin, cp->key());
|
np->setFieldH256(sfNextPageMin, cp->key());
|
||||||
@@ -193,22 +196,22 @@ getPageForToken(
|
|||||||
{
|
{
|
||||||
np->setFieldH256(sfPreviousPageMin, *ppm);
|
np->setFieldH256(sfPreviousPageMin, *ppm);
|
||||||
|
|
||||||
if (auto p3 = view.peek(Keylet(ltNFTOKEN_PAGE, *ppm)))
|
if (NFTokenPageEntryW p3(Keylet(ltNFTOKEN_PAGE, *ppm), view); p3)
|
||||||
{
|
{
|
||||||
p3->setFieldH256(sfNextPageMin, np->key());
|
p3->setFieldH256(sfNextPageMin, np->key());
|
||||||
view.update(p3);
|
p3.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view.insert(np);
|
np.insert();
|
||||||
|
|
||||||
cp->setFieldArray(sfNFTokens, carr);
|
cp->setFieldArray(sfNFTokens, carr);
|
||||||
cp->setFieldH256(sfPreviousPageMin, np->key());
|
cp->setFieldH256(sfPreviousPageMin, np->key());
|
||||||
view.update(cp);
|
cp.update();
|
||||||
|
|
||||||
createCallback(view, owner);
|
createCallback(view, owner);
|
||||||
|
|
||||||
return (first.key < np->key()) ? np : cp;
|
return (first.key < np->key()) ? std::move(np) : std::move(cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -232,7 +235,7 @@ changeTokenURI(
|
|||||||
UInt256 const& nftokenID,
|
UInt256 const& nftokenID,
|
||||||
std::optional<xrpl::Slice> const& uri)
|
std::optional<xrpl::Slice> const& uri)
|
||||||
{
|
{
|
||||||
SLE::pointer const page = locatePage(view, owner, nftokenID);
|
NFTokenPageEntryW page = locatePage(view, owner, nftokenID);
|
||||||
|
|
||||||
// If the page couldn't be found, the given NFT isn't owned by this account
|
// If the page couldn't be found, the given NFT isn't owned by this account
|
||||||
if (!page)
|
if (!page)
|
||||||
@@ -256,7 +259,7 @@ changeTokenURI(
|
|||||||
nftIter->makeFieldAbsent(sfURI);
|
nftIter->makeFieldAbsent(sfURI);
|
||||||
}
|
}
|
||||||
|
|
||||||
view.update(page);
|
page.update();
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +274,7 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft)
|
|||||||
// First, we need to locate the page the NFT belongs to, creating it
|
// First, we need to locate the page the NFT belongs to, creating it
|
||||||
// if necessary. This operation may fail if it is impossible to insert
|
// if necessary. This operation may fail if it is impossible to insert
|
||||||
// the NFT.
|
// the NFT.
|
||||||
SLE::pointer const page =
|
std::optional<NFTokenPageEntryW> page =
|
||||||
getPageForToken(view, owner, nft[sfNFTokenID], [](ApplyView& view, AccountID const& owner) {
|
getPageForToken(view, owner, nft[sfNFTokenID], [](ApplyView& view, AccountID const& owner) {
|
||||||
increaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()});
|
increaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()});
|
||||||
});
|
});
|
||||||
@@ -279,24 +282,13 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft)
|
|||||||
if (!page)
|
if (!page)
|
||||||
return tecNO_SUITABLE_NFTOKEN_PAGE;
|
return tecNO_SUITABLE_NFTOKEN_PAGE;
|
||||||
|
|
||||||
{
|
page->insertToken(std::move(nft));
|
||||||
auto arr = page->getFieldArray(sfNFTokens);
|
|
||||||
arr.pushBack(std::move(nft));
|
|
||||||
|
|
||||||
arr.sort([](STObject const& o1, STObject const& o2) {
|
|
||||||
return compareTokens(o1.getFieldH256(sfNFTokenID), o2.getFieldH256(sfNFTokenID));
|
|
||||||
});
|
|
||||||
|
|
||||||
page->setFieldArray(sfNFTokens, arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
view.update(page);
|
|
||||||
|
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
mergePages(ApplyView& view, SLE::Ref p1, SLE::Ref p2)
|
mergePages(ApplyView& view, NFTokenPageEntryW& p1, NFTokenPageEntryW& p2)
|
||||||
{
|
{
|
||||||
if (p1->key() >= p2->key())
|
if (p1->key() >= p2->key())
|
||||||
Throw<std::runtime_error>("mergePages: pages passed in out of order!");
|
Throw<std::runtime_error>("mergePages: pages passed in out of order!");
|
||||||
@@ -334,19 +326,19 @@ mergePages(ApplyView& view, SLE::Ref p1, SLE::Ref p2)
|
|||||||
|
|
||||||
if (auto const ppm = (*p1)[~sfPreviousPageMin])
|
if (auto const ppm = (*p1)[~sfPreviousPageMin])
|
||||||
{
|
{
|
||||||
auto p0 = view.peek(Keylet(ltNFTOKEN_PAGE, *ppm));
|
NFTokenPageEntryW p0(Keylet(ltNFTOKEN_PAGE, *ppm), view);
|
||||||
|
|
||||||
if (!p0)
|
if (!p0)
|
||||||
Throw<std::runtime_error>("mergePages: p0 can't be located!");
|
Throw<std::runtime_error>("mergePages: p0 can't be located!");
|
||||||
|
|
||||||
p0->setFieldH256(sfNextPageMin, p2->key());
|
p0->setFieldH256(sfNextPageMin, p2->key());
|
||||||
view.update(p0);
|
p0.update();
|
||||||
|
|
||||||
p2->setFieldH256(sfPreviousPageMin, *ppm);
|
p2->setFieldH256(sfPreviousPageMin, *ppm);
|
||||||
}
|
}
|
||||||
|
|
||||||
view.update(p2);
|
p2.update();
|
||||||
view.erase(p1);
|
p1.erase();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -357,7 +349,7 @@ mergePages(ApplyView& view, SLE::Ref p1, SLE::Ref p2)
|
|||||||
TER
|
TER
|
||||||
removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID)
|
removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID)
|
||||||
{
|
{
|
||||||
SLE::pointer const page = locatePage(view, owner, nftokenID);
|
NFTokenPageEntryW page = locatePage(view, owner, nftokenID);
|
||||||
|
|
||||||
// If the page couldn't be found, the given NFT isn't owned by this account
|
// If the page couldn't be found, the given NFT isn't owned by this account
|
||||||
if (!page)
|
if (!page)
|
||||||
@@ -370,7 +362,11 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID)
|
|||||||
* Remove the token from the owner's token directory.
|
* Remove the token from the owner's token directory.
|
||||||
*/
|
*/
|
||||||
TER
|
TER
|
||||||
removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, SLE::Ref curr)
|
removeToken(
|
||||||
|
ApplyView& view,
|
||||||
|
AccountID const& owner,
|
||||||
|
UInt256 const& nftokenID,
|
||||||
|
NFTokenPageEntryW& curr)
|
||||||
{
|
{
|
||||||
// We found a page, but the given NFT may not be in it.
|
// We found a page, but the given NFT may not be in it.
|
||||||
auto arr = curr->getFieldArray(sfNFTokens);
|
auto arr = curr->getFieldArray(sfNFTokens);
|
||||||
@@ -386,14 +382,14 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, S
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Page management:
|
// Page management:
|
||||||
auto const loadPage = [&view](SLE::Ref page1, SF_UINT256 const& field) {
|
auto const loadPage = [&view](NFTokenPageEntryW const& page1, SF_UINT256 const& field) {
|
||||||
SLE::pointer page2;
|
std::optional<NFTokenPageEntryW> page2;
|
||||||
|
|
||||||
if (auto const id = (*page1)[~field])
|
if (auto const id = (*page1)[~field])
|
||||||
{
|
{
|
||||||
page2 = view.peek(Keylet(ltNFTOKEN_PAGE, *id));
|
page2.emplace(Keylet(ltNFTOKEN_PAGE, *id), view);
|
||||||
|
|
||||||
if (!page2)
|
if (!*page2)
|
||||||
{
|
{
|
||||||
Throw<std::runtime_error>(std::format(
|
Throw<std::runtime_error>(std::format(
|
||||||
"page {} has a broken {} field pointing to {}",
|
"page {} has a broken {} field pointing to {}",
|
||||||
@@ -406,8 +402,8 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, S
|
|||||||
return page2;
|
return page2;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto const prev = loadPage(curr, sfPreviousPageMin);
|
auto prev = loadPage(curr, sfPreviousPageMin);
|
||||||
auto const next = loadPage(curr, sfNextPageMin);
|
auto next = loadPage(curr, sfNextPageMin);
|
||||||
|
|
||||||
if (!arr.empty())
|
if (!arr.empty())
|
||||||
{
|
{
|
||||||
@@ -415,14 +411,14 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, S
|
|||||||
// pages. Note that this consolidation attempt may actually merge three
|
// pages. Note that this consolidation attempt may actually merge three
|
||||||
// pages into one!
|
// pages into one!
|
||||||
curr->setFieldArray(sfNFTokens, arr);
|
curr->setFieldArray(sfNFTokens, arr);
|
||||||
view.update(curr);
|
curr.update();
|
||||||
|
|
||||||
std::uint32_t cnt = 0;
|
std::uint32_t cnt = 0;
|
||||||
|
|
||||||
if (prev && mergePages(view, prev, curr))
|
if (prev && mergePages(view, *prev, curr))
|
||||||
++cnt;
|
++cnt;
|
||||||
|
|
||||||
if (next && mergePages(view, curr, next))
|
if (next && mergePages(view, curr, *next))
|
||||||
++cnt;
|
++cnt;
|
||||||
|
|
||||||
if (cnt != 0)
|
if (cnt != 0)
|
||||||
@@ -446,16 +442,17 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, S
|
|||||||
((curr->key() & nft::kPageMask) == kPageMask))
|
((curr->key() & nft::kPageMask) == kPageMask))
|
||||||
{
|
{
|
||||||
// Copy all relevant information from prev to curr.
|
// Copy all relevant information from prev to curr.
|
||||||
curr->peekFieldArray(sfNFTokens) = prev->peekFieldArray(sfNFTokens);
|
curr->peekFieldArray(sfNFTokens) = (*prev)->peekFieldArray(sfNFTokens);
|
||||||
|
|
||||||
if (auto const prevLink = prev->at(~sfPreviousPageMin))
|
if (auto const prevLink = (*prev)->at(~sfPreviousPageMin))
|
||||||
{
|
{
|
||||||
curr->at(sfPreviousPageMin) = *prevLink;
|
curr->at(sfPreviousPageMin) = *prevLink;
|
||||||
|
|
||||||
// Also fix up the NextPageMin link in the new Previous.
|
// Also fix up the NextPageMin link in the new Previous.
|
||||||
auto const newPrev = loadPage(curr, sfPreviousPageMin);
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) link set above
|
||||||
|
auto newPrev = loadPage(curr, sfPreviousPageMin).value();
|
||||||
newPrev->at(sfNextPageMin) = curr->key();
|
newPrev->at(sfNextPageMin) = curr->key();
|
||||||
view.update(newPrev);
|
newPrev.update();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -464,8 +461,8 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, S
|
|||||||
|
|
||||||
decreaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()});
|
decreaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()});
|
||||||
|
|
||||||
view.update(curr);
|
curr.update();
|
||||||
view.erase(prev);
|
prev->erase();
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,14 +470,14 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, S
|
|||||||
// and then remove it.
|
// and then remove it.
|
||||||
if (next)
|
if (next)
|
||||||
{
|
{
|
||||||
prev->setFieldH256(sfNextPageMin, next->key());
|
(*prev)->setFieldH256(sfNextPageMin, (*next)->key());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
prev->makeFieldAbsent(sfNextPageMin);
|
(*prev)->makeFieldAbsent(sfNextPageMin);
|
||||||
}
|
}
|
||||||
|
|
||||||
view.update(prev);
|
prev->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (next)
|
if (next)
|
||||||
@@ -488,17 +485,17 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, S
|
|||||||
// Make our next page point to our previous page:
|
// Make our next page point to our previous page:
|
||||||
if (prev)
|
if (prev)
|
||||||
{
|
{
|
||||||
next->setFieldH256(sfPreviousPageMin, prev->key());
|
(*next)->setFieldH256(sfPreviousPageMin, (*prev)->key());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
next->makeFieldAbsent(sfPreviousPageMin);
|
(*next)->makeFieldAbsent(sfPreviousPageMin);
|
||||||
}
|
}
|
||||||
|
|
||||||
view.update(next);
|
next->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
view.erase(curr);
|
curr.erase();
|
||||||
|
|
||||||
uint32_t cnt = 1;
|
uint32_t cnt = 1;
|
||||||
|
|
||||||
@@ -510,12 +507,13 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, S
|
|||||||
//
|
//
|
||||||
// But, in case that analysis is wrong, it's good to leave this code here
|
// But, in case that analysis is wrong, it's good to leave this code here
|
||||||
// just in case.
|
// just in case.
|
||||||
if (prev && next &&
|
if (prev && next)
|
||||||
mergePages(
|
{
|
||||||
view,
|
NFTokenPageEntryW p1(Keylet(ltNFTOKEN_PAGE, (*prev)->key()), view);
|
||||||
view.peek(Keylet(ltNFTOKEN_PAGE, prev->key())),
|
NFTokenPageEntryW p2(Keylet(ltNFTOKEN_PAGE, (*next)->key()), view);
|
||||||
view.peek(Keylet(ltNFTOKEN_PAGE, next->key()))))
|
if (mergePages(view, p1, p2))
|
||||||
cnt++;
|
cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
decreaseOwnerCount(view, owner, {}, cnt, beast::Journal{beast::Journal::getNullSink()});
|
decreaseOwnerCount(view, owner, {}, cnt, beast::Journal{beast::Journal::getNullSink()});
|
||||||
|
|
||||||
@@ -525,39 +523,30 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, S
|
|||||||
std::optional<STObject>
|
std::optional<STObject>
|
||||||
findToken(ReadView const& view, AccountID const& owner, UInt256 const& nftokenID)
|
findToken(ReadView const& view, AccountID const& owner, UInt256 const& nftokenID)
|
||||||
{
|
{
|
||||||
SLE::const_pointer const page = locatePage(view, owner, nftokenID);
|
NFTokenPageEntryR const page = locatePage(view, owner, nftokenID);
|
||||||
|
|
||||||
// If the page couldn't be found, the given NFT isn't owned by this account
|
// If the page couldn't be found, the given NFT isn't owned by this account
|
||||||
if (!page)
|
if (!page)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
// We found a candidate page, but the given NFT may not be in it.
|
// We found a candidate page, but the given NFT may not be in it.
|
||||||
for (auto const& t : page->getFieldArray(sfNFTokens))
|
return page.findToken(nftokenID);
|
||||||
{
|
|
||||||
if (t[sfNFTokenID] == nftokenID)
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TokenAndPage>
|
std::optional<TokenAndPage>
|
||||||
findTokenAndPage(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID)
|
findTokenAndPage(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID)
|
||||||
{
|
{
|
||||||
SLE::pointer page = locatePage(view, owner, nftokenID);
|
NFTokenPageEntryW page = locatePage(view, owner, nftokenID);
|
||||||
|
|
||||||
// If the page couldn't be found, the given NFT isn't owned by this account
|
// If the page couldn't be found, the given NFT isn't owned by this account
|
||||||
if (!page)
|
if (!page)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
// We found a candidate page, but the given NFT may not be in it.
|
// We found a candidate page, but the given NFT may not be in it.
|
||||||
for (auto const& t : page->getFieldArray(sfNFTokens))
|
if (auto token = page.findToken(nftokenID))
|
||||||
{
|
{
|
||||||
if (t[sfNFTokenID] == nftokenID)
|
// This std::optional constructor is explicit, so it is spelled out.
|
||||||
{
|
return std::optional<TokenAndPage>(std::in_place, std::move(*token), std::move(page));
|
||||||
// This std::optional constructor is explicit, so it is spelled out.
|
|
||||||
return std::optional<TokenAndPage>(std::in_place, t, std::move(page));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -733,7 +722,7 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner)
|
|||||||
nextPage->at(sfPreviousPageMin) = *prevLink;
|
nextPage->at(sfPreviousPageMin) = *prevLink;
|
||||||
|
|
||||||
// Also fix up the NextPageMin link in the new Previous.
|
// Also fix up the NextPageMin link in the new Previous.
|
||||||
auto const newPrev = view.peek(Keylet(ltNFTOKEN_PAGE, *prevLink));
|
NFTokenPageEntryW newPrev(Keylet(ltNFTOKEN_PAGE, *prevLink), view);
|
||||||
if (!newPrev)
|
if (!newPrev)
|
||||||
{
|
{
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
@@ -743,7 +732,7 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner)
|
|||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
newPrev->at(sfNextPageMin) = nextPage->key();
|
newPrev->at(sfNextPageMin) = nextPage->key();
|
||||||
view.update(newPrev);
|
newPrev.update();
|
||||||
}
|
}
|
||||||
view.erase(page);
|
view.erase(page);
|
||||||
view.insert(nextPage);
|
view.insert(nextPage);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <xrpl/ledger/ApplyView.h>
|
#include <xrpl/ledger/ApplyView.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/ledger/View.h>
|
#include <xrpl/ledger/View.h>
|
||||||
|
#include <xrpl/ledger/entries/NFTokenPageEntry.h>
|
||||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
|
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
|
||||||
@@ -264,8 +265,9 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
|||||||
Keylet const first = keylet::nftokenPageMin(account);
|
Keylet const first = keylet::nftokenPageMin(account);
|
||||||
Keylet const last = keylet::nftokenPageMax(account);
|
Keylet const last = keylet::nftokenPageMax(account);
|
||||||
|
|
||||||
auto const cp = ctx.view.read(
|
NFTokenPageEntryR const cp(
|
||||||
Keylet(ltNFTOKEN_PAGE, ctx.view.succ(first.key, last.key.next()).value_or(last.key)));
|
Keylet(ltNFTOKEN_PAGE, ctx.view.succ(first.key, last.key.next()).value_or(last.key)),
|
||||||
|
ctx.view);
|
||||||
if (cp)
|
if (cp)
|
||||||
return tecHAS_OBLIGATIONS;
|
return tecHAS_OBLIGATIONS;
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include <xrpl/ledger/entries/XChainOwnedClaimIDEntry.h> // IWYU pragma: keep
|
#include <xrpl/ledger/entries/XChainOwnedClaimIDEntry.h> // IWYU pragma: keep
|
||||||
#include <xrpl/ledger/entries/XChainOwnedCreateAccountClaimIDEntry.h> // IWYU pragma: keep
|
#include <xrpl/ledger/entries/XChainOwnedCreateAccountClaimIDEntry.h> // IWYU pragma: keep
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
|
#include <xrpl/protocol/Keylet.h>
|
||||||
#include <xrpl/protocol/LedgerFormats.h>
|
#include <xrpl/protocol/LedgerFormats.h>
|
||||||
#include <xrpl/protocol/SField.h>
|
#include <xrpl/protocol/SField.h>
|
||||||
#include <xrpl/protocol/STLedgerEntry.h>
|
#include <xrpl/protocol/STLedgerEntry.h>
|
||||||
@@ -76,12 +77,22 @@ template class SLEBase<ApplyView>;
|
|||||||
#pragma push_macro("LEDGER_ENTRY")
|
#pragma push_macro("LEDGER_ENTRY")
|
||||||
#undef LEDGER_ENTRY
|
#undef LEDGER_ENTRY
|
||||||
|
|
||||||
|
// No explicit instantiation here, neither a definition nor a declaration.
|
||||||
|
// A definition would instantiate an entry a second time once its member
|
||||||
|
// bodies live in its own .cpp (MSVC rejects that with C4661 under /WX), and a
|
||||||
|
// declaration (`extern template`) stops clang from emitting the inline
|
||||||
|
// constructors of the header-only entries at -O0, which leaves them undefined
|
||||||
|
// at link time in the debug builds. Naming the class in a static_assert is
|
||||||
|
// enough to instantiate its definition, which is all this exhaustiveness
|
||||||
|
// check needs; the per-type suites exercise the members.
|
||||||
#define LEDGER_ENTRY(tag, value, name, ...) \
|
#define LEDGER_ENTRY(tag, value, name, ...) \
|
||||||
template class name##Entry<ReadView>; \
|
|
||||||
template class name##Entry<ApplyView>; \
|
|
||||||
static_assert( \
|
static_assert( \
|
||||||
name##Entry<ReadView>::kEntryType == tag && name##Entry<ApplyView>::kEntryType == tag, \
|
name##Entry<ReadView>::kEntryType == tag && name##Entry<ApplyView>::kEntryType == tag, \
|
||||||
#name "Entry must be bound to " #tag);
|
#name "Entry must be bound to " #tag); \
|
||||||
|
static_assert( \
|
||||||
|
std::is_constructible_v<name##Entry<ReadView>, Keylet const&, ReadView const&> && \
|
||||||
|
std::is_constructible_v<name##Entry<ApplyView>, Keylet const&, ApplyView&>, \
|
||||||
|
#name "Entry must be constructible from a Keylet and a view");
|
||||||
|
|
||||||
#include <xrpl/protocol/detail/ledger_entries.macro>
|
#include <xrpl/protocol/detail/ledger_entries.macro>
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user