1#include <xrpl/tx/invariants/NFTInvariant.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/beast/utility/Journal.h>
6#include <xrpl/beast/utility/Zero.h>
7#include <xrpl/ledger/ReadView.h>
8#include <xrpl/ledger/helpers/NFTokenHelpers.h>
9#include <xrpl/protocol/Feature.h>
10#include <xrpl/protocol/LedgerFormats.h>
11#include <xrpl/protocol/Protocol.h>
12#include <xrpl/protocol/SField.h>
13#include <xrpl/protocol/STArray.h>
14#include <xrpl/protocol/STLedgerEntry.h>
15#include <xrpl/protocol/STTx.h>
16#include <xrpl/protocol/TER.h>
17#include <xrpl/protocol/TxFormats.h>
18#include <xrpl/protocol/XRPAmount.h>
19#include <xrpl/protocol/nftPageMask.h>
20#include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
31 static constexpr uint256 kAccountBits = ~kPageBits;
33 if ((before && before->getType() != ltNFTOKEN_PAGE) ||
38 uint256 const account = sle->
key() & kAccountBits;
39 uint256 const hiLimit = sle->key() & kPageBits;
47 if (account != (*prev & kAccountBits))
50 if (hiLimit <= (*prev & kPageBits))
54 if (
auto const next = (*sle)[~sfNextPageMin])
56 if (account != (*next & kAccountBits))
59 if (hiLimit >= (*next & kPageBits))
64 auto const& nftokens = sle->getFieldArray(sfNFTokens);
67 if (
std::size_t const nftokenCount = nftokens.size();
73 uint256 const loLimit = prev ? *prev & kPageBits :
uint256(beast::kZero);
77 for (
auto const& obj : nftokens)
79 uint256 const tokenID = obj[sfNFTokenID];
86 if (
uint256 const tokenPageBits = tokenID & kPageBits;
87 tokenPageBits < loLimit || tokenPageBits >= hiLimit)
90 if (
auto uri = obj[~sfURI]; uri && uri->empty())
104 before->isFieldPresent(sfPreviousPageMin))
113 if (!isDelete && before &&
after)
121 before->isFieldPresent(sfNextPageMin) && !
after->isFieldPresent(sfNextPageMin))
138 JLOG(j.
fatal()) <<
"Invariant failed: NFT page is improperly linked.";
144 JLOG(j.
fatal()) <<
"Invariant failed: NFT found in incorrect page.";
150 JLOG(j.
fatal()) <<
"Invariant failed: NFTs on page are not sorted.";
156 JLOG(j.
fatal()) <<
"Invariant failed: NFT contains empty URI.";
162 JLOG(j.
fatal()) <<
"Invariant failed: NFT page has invalid size.";
170 JLOG(j.
fatal()) <<
"Invariant failed: Last NFT page deleted with "
171 "non-empty directory.";
176 JLOG(j.
fatal()) <<
"Invariant failed: Lost NextMinPage link.";
188 if (before && before->getType() == ltACCOUNT_ROOT)
194 if (
after &&
after->getType() == ltACCOUNT_ROOT)
213 JLOG(j.
fatal()) <<
"Invariant failed: the number of minted tokens "
214 "changed without a mint transaction!";
220 JLOG(j.
fatal()) <<
"Invariant failed: the number of burned tokens "
221 "changed without a burn transaction!";
232 JLOG(j.
fatal()) <<
"Invariant failed: successful minting didn't increase "
233 "the number of minted tokens.";
239 JLOG(j.
fatal()) <<
"Invariant failed: failed minting changed the "
240 "number of minted tokens.";
246 JLOG(j.
fatal()) <<
"Invariant failed: minting changed the number of "
258 JLOG(j.
fatal()) <<
"Invariant failed: successful burning didn't increase "
259 "the number of burned tokens.";
266 JLOG(j.
fatal()) <<
"Invariant failed: failed burning changed the "
267 "number of burned tokens.";
273 JLOG(j.
fatal()) <<
"Invariant failed: burning changed the number of "
A generic endpoint for log messages.
std::uint32_t afterMintedTotal_
std::uint32_t beforeBurnedTotal_
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
std::uint32_t beforeMintedTotal_
std::uint32_t afterBurnedTotal_
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
std::shared_ptr< STLedgerEntry const > const & const_ref
TxType getTxnType() const
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
constexpr uint256 kPageMask(std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff"))
bool compareTokens(uint256 const &a, uint256 const &b)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
bool hasPrivilege(STTx const &tx, Privilege priv)
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
constexpr std::size_t kDirMaxTokensPerPage
The maximum number of items in an NFT page.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER