rippled
Loading...
Searching...
No Matches
NFTokenBurn.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2021 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/tx/detail/NFTokenBurn.h>
21#include <xrpld/app/tx/detail/NFTokenUtils.h>
22#include <xrpld/ledger/View.h>
23#include <xrpl/protocol/Feature.h>
24#include <xrpl/protocol/Protocol.h>
25#include <xrpl/protocol/TxFlags.h>
26#include <xrpl/protocol/st.h>
27#include <boost/endian/conversion.hpp>
28#include <array>
29
30namespace ripple {
31
34{
35 if (!ctx.rules.enabled(featureNonFungibleTokensV1))
36 return temDISABLED;
37
38 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
39 return ret;
40
41 if (ctx.tx.getFlags() & tfUniversalMask)
42 return temINVALID_FLAG;
43
44 return preflight2(ctx);
45}
46
47TER
49{
50 auto const owner = [&ctx]() {
51 if (ctx.tx.isFieldPresent(sfOwner))
52 return ctx.tx.getAccountID(sfOwner);
53
54 return ctx.tx[sfAccount];
55 }();
56
57 if (!nft::findToken(ctx.view, owner, ctx.tx[sfNFTokenID]))
58 return tecNO_ENTRY;
59
60 // The owner of a token can always burn it, but the issuer can only
61 // do so if the token is marked as burnable.
62 if (auto const account = ctx.tx[sfAccount]; owner != account)
63 {
64 if (!(nft::getFlags(ctx.tx[sfNFTokenID]) & nft::flagBurnable))
65 return tecNO_PERMISSION;
66
67 if (auto const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]);
68 issuer != account)
69 {
70 if (auto const sle = ctx.view.read(keylet::account(issuer)); sle)
71 {
72 if (auto const minter = (*sle)[~sfNFTokenMinter];
73 minter != account)
74 return tecNO_PERMISSION;
75 }
76 }
77 }
78
79 if (!ctx.view.rules().enabled(fixNonFungibleTokensV1_2))
80 {
81 // If there are too many offers, then burning the token would produce
82 // too much metadata. Disallow burning a token with too many offers.
83 return nft::notTooManyOffers(ctx.view, ctx.tx[sfNFTokenID]);
84 }
85
86 return tesSUCCESS;
87}
88
89TER
91{
92 // Remove the token, effectively burning it:
93 auto const ret = nft::removeToken(
94 view(),
95 ctx_.tx.isFieldPresent(sfOwner) ? ctx_.tx.getAccountID(sfOwner)
96 : ctx_.tx.getAccountID(sfAccount),
97 ctx_.tx[sfNFTokenID]);
98
99 // Should never happen since preclaim() verified the token is present.
100 if (!isTesSuccess(ret))
101 return ret;
102
103 if (auto issuer =
104 view().peek(keylet::account(nft::getIssuer(ctx_.tx[sfNFTokenID]))))
105 {
106 (*issuer)[~sfBurnedNFTokens] =
107 (*issuer)[~sfBurnedNFTokens].value_or(0) + 1;
108 view().update(issuer);
109 }
110
111 if (ctx_.view().rules().enabled(fixNonFungibleTokensV1_2))
112 {
113 // Delete up to 500 offers in total.
114 // Because the number of sell offers is likely to be less than
115 // the number of buy offers, we prioritize the deletion of sell
116 // offers in order to clean up sell offer directory
117 std::size_t const deletedSellOffers = nft::removeTokenOffersWithLimit(
118 view(),
119 keylet::nft_sells(ctx_.tx[sfNFTokenID]),
121
122 if (maxDeletableTokenOfferEntries > deletedSellOffers)
123 {
125 view(),
126 keylet::nft_buys(ctx_.tx[sfNFTokenID]),
127 maxDeletableTokenOfferEntries - deletedSellOffers);
128 }
129 }
130 else
131 {
132 // Deletion of all offers.
134 view(),
135 keylet::nft_sells(ctx_.tx[sfNFTokenID]),
137
139 view(),
140 keylet::nft_buys(ctx_.tx[sfNFTokenID]),
142 }
143
144 return tesSUCCESS;
145}
146
147} // namespace ripple
ApplyView & view()
Definition: ApplyContext.h:54
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
static TER preclaim(PreclaimContext const &ctx)
Definition: NFTokenBurn.cpp:48
TER doApply() override
Definition: NFTokenBurn.cpp:90
static NotTEC preflight(PreflightContext const &ctx)
Definition: NFTokenBurn.cpp:33
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:122
AccountID getAccountID(SField const &field) const
Definition: STObject.cpp:621
bool isFieldPresent(SField const &field) const
Definition: STObject.cpp:454
std::uint32_t getFlags() const
Definition: STObject.cpp:507
ApplyView & view()
Definition: Transactor.h:107
ApplyContext & ctx_
Definition: Transactor.h:88
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:160
Keylet nft_buys(uint256 const &id) noexcept
The directory of buy offers for the specified NFT.
Definition: Indexes.cpp:410
Keylet nft_sells(uint256 const &id) noexcept
The directory of sell offers for the specified NFT.
Definition: Indexes.cpp:416
constexpr std::uint16_t const flagBurnable
Definition: nft.h:53
std::uint16_t getFlags(uint256 const &id)
Definition: nft.h:60
TER removeToken(ApplyView &view, AccountID const &owner, uint256 const &nftokenID)
Remove the token from the owner's token directory.
AccountID getIssuer(uint256 const &id)
Definition: nft.h:120
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.
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.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::size_t constexpr maxDeletableTokenOfferEntries
The maximum number of offers in an offer directory for NFT to be burnable.
Definition: Protocol.h:71
bool isTesSuccess(TER x)
Definition: TER.h:656
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:82
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:134
@ tecNO_ENTRY
Definition: TER.h:293
@ tecNO_PERMISSION
Definition: TER.h:292
@ tesSUCCESS
Definition: TER.h:242
constexpr std::uint32_t tfUniversalMask
Definition: TxFlags.h:62
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:587
@ temINVALID_FLAG
Definition: TER.h:111
@ temDISABLED
Definition: TER.h:114
State information when determining if a tx is likely to claim a fee.
Definition: Transactor.h:53
ReadView const & view
Definition: Transactor.h:56
State information when preflighting a tx.
Definition: Transactor.h:32