rippled
Loading...
Searching...
No Matches
DeleteAccount.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2019 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/DID.h>
21#include <xrpld/app/tx/detail/DelegateSet.h>
22#include <xrpld/app/tx/detail/DeleteAccount.h>
23#include <xrpld/app/tx/detail/DeleteOracle.h>
24#include <xrpld/app/tx/detail/DepositPreauth.h>
25#include <xrpld/app/tx/detail/NFTokenUtils.h>
26#include <xrpld/app/tx/detail/SetSignerList.h>
27
28#include <xrpl/basics/Log.h>
29#include <xrpl/basics/mulDiv.h>
30#include <xrpl/beast/utility/instrumentation.h>
31#include <xrpl/ledger/CredentialHelpers.h>
32#include <xrpl/ledger/View.h>
33#include <xrpl/protocol/Feature.h>
34#include <xrpl/protocol/Indexes.h>
35#include <xrpl/protocol/Protocol.h>
36#include <xrpl/protocol/TxFlags.h>
37#include <xrpl/protocol/Units.h>
38
39namespace ripple {
40
43{
44 if (!ctx.rules.enabled(featureDeletableAccounts))
45 return temDISABLED;
46
47 if (ctx.tx.isFieldPresent(sfCredentialIDs) &&
48 !ctx.rules.enabled(featureCredentials))
49 return temDISABLED;
50
51 if (ctx.tx.getFlags() & tfUniversalMask)
52 return temINVALID_FLAG;
53
54 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
55 return ret;
56
57 if (ctx.tx[sfAccount] == ctx.tx[sfDestination])
58 // An account cannot be deleted and give itself the resulting XRP.
59 return temDST_IS_SRC;
60
61 if (auto const err = credentials::checkFields(ctx.tx, ctx.j);
62 !isTesSuccess(err))
63 return err;
64
65 return preflight2(ctx);
66}
67
70{
71 // The fee required for AccountDelete is one owner reserve.
72 return view.fees().increment;
73}
74
75namespace {
76// Define a function pointer type that can be used to delete ledger node types.
77using DeleterFuncPtr = TER (*)(
78 Application& app,
79 ApplyView& view,
80 AccountID const& account,
81 uint256 const& delIndex,
82 std::shared_ptr<SLE> const& sleDel,
84
85// Local function definitions that provides signature compatibility.
86TER
88 Application& app,
89 ApplyView& view,
90 AccountID const& account,
91 uint256 const& delIndex,
92 std::shared_ptr<SLE> const& sleDel,
94{
95 return offerDelete(view, sleDel, j);
96}
97
98TER
100 Application& app,
101 ApplyView& view,
102 AccountID const& account,
103 uint256 const& delIndex,
104 std::shared_ptr<SLE> const& sleDel,
106{
107 return SetSignerList::removeFromLedger(app, view, account, j);
108}
109
110TER
111removeTicketFromLedger(
112 Application&,
113 ApplyView& view,
114 AccountID const& account,
115 uint256 const& delIndex,
118{
119 return Transactor::ticketDelete(view, account, delIndex, j);
120}
121
122TER
123removeDepositPreauthFromLedger(
124 Application&,
125 ApplyView& view,
126 AccountID const&,
127 uint256 const& delIndex,
130{
131 return DepositPreauth::removeFromLedger(view, delIndex, j);
132}
133
134TER
135removeNFTokenOfferFromLedger(
136 Application& app,
137 ApplyView& view,
138 AccountID const& account,
139 uint256 const& delIndex,
140 std::shared_ptr<SLE> const& sleDel,
142{
143 if (!nft::deleteTokenOffer(view, sleDel))
144 return tefBAD_LEDGER;
145
146 return tesSUCCESS;
147}
148
149TER
150removeDIDFromLedger(
151 Application& app,
152 ApplyView& view,
153 AccountID const& account,
154 uint256 const& delIndex,
155 std::shared_ptr<SLE> const& sleDel,
157{
158 return DIDDelete::deleteSLE(view, sleDel, account, j);
159}
160
161TER
162removeOracleFromLedger(
163 Application&,
164 ApplyView& view,
165 AccountID const& account,
166 uint256 const&,
167 std::shared_ptr<SLE> const& sleDel,
169{
170 return DeleteOracle::deleteOracle(view, sleDel, account, j);
171}
172
173TER
174removeCredentialFromLedger(
175 Application&,
176 ApplyView& view,
177 AccountID const&,
178 uint256 const&,
179 std::shared_ptr<SLE> const& sleDel,
181{
182 return credentials::deleteSLE(view, sleDel, j);
183}
184
185TER
186removeDelegateFromLedger(
187 Application& app,
188 ApplyView& view,
189 AccountID const& account,
190 uint256 const& delIndex,
191 std::shared_ptr<SLE> const& sleDel,
193{
194 return DelegateSet::deleteDelegate(view, sleDel, account, j);
195}
196
197// Return nullptr if the LedgerEntryType represents an obligation that can't
198// be deleted. Otherwise return the pointer to the function that can delete
199// the non-obligation
200DeleterFuncPtr
201nonObligationDeleter(LedgerEntryType t)
202{
203 switch (t)
204 {
205 case ltOFFER:
206 return offerDelete;
207 case ltSIGNER_LIST:
209 case ltTICKET:
210 return removeTicketFromLedger;
211 case ltDEPOSIT_PREAUTH:
212 return removeDepositPreauthFromLedger;
213 case ltNFTOKEN_OFFER:
214 return removeNFTokenOfferFromLedger;
215 case ltDID:
216 return removeDIDFromLedger;
217 case ltORACLE:
218 return removeOracleFromLedger;
219 case ltCREDENTIAL:
220 return removeCredentialFromLedger;
221 case ltDELEGATE:
222 return removeDelegateFromLedger;
223 default:
224 return nullptr;
225 }
226}
227
228} // namespace
229
230TER
232{
233 AccountID const account{ctx.tx[sfAccount]};
234 AccountID const dst{ctx.tx[sfDestination]};
235
236 auto sleDst = ctx.view.read(keylet::account(dst));
237
238 if (!sleDst)
239 return tecNO_DST;
240
241 if ((*sleDst)[sfFlags] & lsfRequireDestTag && !ctx.tx[~sfDestinationTag])
242 return tecDST_TAG_NEEDED;
243
244 // If credentials are provided - check them anyway
245 if (auto const err = credentials::valid(ctx.tx, ctx.view, account, ctx.j);
246 !isTesSuccess(err))
247 return err;
248
249 // if credentials then postpone auth check to doApply, to check for expired
250 // credentials
251 if (!ctx.tx.isFieldPresent(sfCredentialIDs))
252 {
253 // Check whether the destination account requires deposit authorization.
254 if (ctx.view.rules().enabled(featureDepositAuth) &&
255 (sleDst->getFlags() & lsfDepositAuth))
256 {
257 if (!ctx.view.exists(keylet::depositPreauth(dst, account)))
258 return tecNO_PERMISSION;
259 }
260 }
261
262 auto sleAccount = ctx.view.read(keylet::account(account));
263 XRPL_ASSERT(
264 sleAccount, "ripple::DeleteAccount::preclaim : non-null account");
265 if (!sleAccount)
266 return terNO_ACCOUNT;
267
268 if (ctx.view.rules().enabled(featureNonFungibleTokensV1))
269 {
270 // If an issuer has any issued NFTs resident in the ledger then it
271 // cannot be deleted.
272 if ((*sleAccount)[~sfMintedNFTokens] !=
273 (*sleAccount)[~sfBurnedNFTokens])
274 return tecHAS_OBLIGATIONS;
275
276 // If the account owns any NFTs it cannot be deleted.
277 Keylet const first = keylet::nftpage_min(account);
278 Keylet const last = keylet::nftpage_max(account);
279
280 auto const cp = ctx.view.read(Keylet(
281 ltNFTOKEN_PAGE,
282 ctx.view.succ(first.key, last.key.next()).value_or(last.key)));
283 if (cp)
284 return tecHAS_OBLIGATIONS;
285 }
286
287 // We don't allow an account to be deleted if its sequence number
288 // is within 256 of the current ledger. This prevents replay of old
289 // transactions if this account is resurrected after it is deleted.
290 //
291 // We look at the account's Sequence rather than the transaction's
292 // Sequence in preparation for Tickets.
293 constexpr std::uint32_t seqDelta{255};
294 if ((*sleAccount)[sfSequence] + seqDelta > ctx.view.seq())
295 return tecTOO_SOON;
296
297 // When fixNFTokenRemint is enabled, we don't allow an account to be
298 // deleted if <FirstNFTokenSequence + MintedNFTokens> is within 256 of the
299 // current ledger. This is to prevent having duplicate NFTokenIDs after
300 // account re-creation.
301 //
302 // Without this restriction, duplicate NFTokenIDs can be reproduced when
303 // authorized minting is involved. Because when the minter mints a NFToken,
304 // the issuer's sequence does not change. So when the issuer re-creates
305 // their account and mints a NFToken, it is possible that the
306 // NFTokenSequence of this NFToken is the same as the one that the
307 // authorized minter minted in a previous ledger.
308 if (ctx.view.rules().enabled(fixNFTokenRemint) &&
309 ((*sleAccount)[~sfFirstNFTokenSequence].value_or(0) +
310 (*sleAccount)[~sfMintedNFTokens].value_or(0) + seqDelta >
311 ctx.view.seq()))
312 return tecTOO_SOON;
313
314 // Verify that the account does not own any objects that would prevent
315 // the account from being deleted.
316 Keylet const ownerDirKeylet{keylet::ownerDir(account)};
317 if (dirIsEmpty(ctx.view, ownerDirKeylet))
318 return tesSUCCESS;
319
320 std::shared_ptr<SLE const> sleDirNode{};
321 unsigned int uDirEntry{0};
322 uint256 dirEntry{beast::zero};
323
324 // Account has no directory at all. This _should_ have been caught
325 // by the dirIsEmpty() check earlier, but it's okay to catch it here.
326 if (!cdirFirst(
327 ctx.view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
328 return tesSUCCESS;
329
330 std::int32_t deletableDirEntryCount{0};
331 do
332 {
333 // Make sure any directory node types that we find are the kind
334 // we can delete.
335 auto sleItem = ctx.view.read(keylet::child(dirEntry));
336 if (!sleItem)
337 {
338 // Directory node has an invalid index. Bail out.
339 JLOG(ctx.j.fatal())
340 << "DeleteAccount: directory node in ledger " << ctx.view.seq()
341 << " has index to object that is missing: "
342 << to_string(dirEntry);
343 return tefBAD_LEDGER;
344 }
345
346 LedgerEntryType const nodeType{
347 safe_cast<LedgerEntryType>((*sleItem)[sfLedgerEntryType])};
348
349 if (!nonObligationDeleter(nodeType))
350 return tecHAS_OBLIGATIONS;
351
352 // We found a deletable directory entry. Count it. If we find too
353 // many deletable directory entries then bail out.
354 if (++deletableDirEntryCount > maxDeletableDirEntries)
355 return tefTOO_BIG;
356
357 } while (cdirNext(
358 ctx.view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
359
360 return tesSUCCESS;
361}
362
363TER
365{
366 auto src = view().peek(keylet::account(account_));
367 XRPL_ASSERT(
368 src, "ripple::DeleteAccount::doApply : non-null source account");
369
370 auto const dstID = ctx_.tx[sfDestination];
371 auto dst = view().peek(keylet::account(dstID));
372 XRPL_ASSERT(
373 dst, "ripple::DeleteAccount::doApply : non-null destination account");
374
375 if (!src || !dst)
376 return tefBAD_LEDGER;
377
378 if (ctx_.view().rules().enabled(featureDepositAuth) &&
379 ctx_.tx.isFieldPresent(sfCredentialIDs))
380 {
381 if (auto err = verifyDepositPreauth(
382 ctx_.tx, ctx_.view(), account_, dstID, dst, ctx_.journal);
383 !isTesSuccess(err))
384 return err;
385 }
386
387 Keylet const ownerDirKeylet{keylet::ownerDir(account_)};
388 auto const ter = cleanupOnAccountDelete(
389 view(),
390 ownerDirKeylet,
391 [&](LedgerEntryType nodeType,
392 uint256 const& dirEntry,
394 if (auto deleter = nonObligationDeleter(nodeType))
395 {
396 TER const result{
397 deleter(ctx_.app, view(), account_, dirEntry, sleItem, j_)};
398
399 return {result, SkipEntry::No};
400 }
401
402 UNREACHABLE(
403 "ripple::DeleteAccount::doApply : undeletable item not found "
404 "in preclaim");
405 JLOG(j_.error()) << "DeleteAccount undeletable item not "
406 "found in preclaim.";
408 },
409 ctx_.journal);
410 if (ter != tesSUCCESS)
411 return ter;
412
413 // Transfer any XRP remaining after the fee is paid to the destination:
414 (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance;
415 (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance;
417
418 XRPL_ASSERT(
419 (*src)[sfBalance] == XRPAmount(0),
420 "ripple::DeleteAccount::doApply : source balance is zero");
421
422 // If there's still an owner directory associated with the source account
423 // delete it.
424 if (view().exists(ownerDirKeylet) && !view().emptyDirDelete(ownerDirKeylet))
425 {
426 JLOG(j_.error()) << "DeleteAccount cannot delete root dir node of "
427 << toBase58(account_);
428 return tecHAS_OBLIGATIONS;
429 }
430
431 // Re-arm the password change fee if we can and need to.
432 if (mSourceBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
433 dst->clearFlag(lsfPasswordSpent);
434
435 view().update(dst);
436 view().erase(src);
437
438 return tesSUCCESS;
439}
440
441} // namespace ripple
A generic endpoint for log messages.
Definition Journal.h:60
Stream fatal() const
Definition Journal.h:352
Stream error() const
Definition Journal.h:346
ApplyView & view()
Application & app
beast::Journal const journal
void deliver(STAmount const &amount)
Sets the DeliveredAmount field in the metadata.
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:143
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
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.
static TER deleteSLE(ApplyContext &ctx, Keylet sleKeylet, AccountID const owner)
Definition DID.cpp:190
static TER deleteDelegate(ApplyView &view, std::shared_ptr< SLE > const &sle, AccountID const &account, beast::Journal j)
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
static TER deleteOracle(ApplyView &view, std::shared_ptr< SLE > const &sle, AccountID const &account, beast::Journal j)
static TER removeFromLedger(ApplyView &view, uint256 const &delIndex, beast::Journal j)
A view into a ledger.
Definition ReadView.h:51
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.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:118
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:130
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:484
std::uint32_t getFlags() const
Definition STObject.cpp:537
static TER removeFromLedger(Application &app, ApplyView &view, AccountID const &account, beast::Journal j)
AccountID const account_
Definition Transactor.h:145
ApplyView & view()
Definition Transactor.h:161
beast::Journal const j_
Definition Transactor.h:143
static TER ticketDelete(ApplyView &view, AccountID const &account, uint256 const &ticketIndex, beast::Journal j)
XRPAmount mSourceBalance
Definition Transactor.h:147
ApplyContext & ctx_
Definition Transactor.h:141
base_uint next() const
Definition base_uint.h:455
NotTEC checkFields(STTx const &tx, beast::Journal j)
TER deleteSLE(ApplyView &view, std::shared_ptr< SLE > const &sleCredential, beast::Journal j)
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition Indexes.cpp:190
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Keylet nftpage_min(AccountID const &owner)
NFT page keylets.
Definition Indexes.cpp:403
Keylet nftpage_max(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Definition Indexes.cpp:411
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:374
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition Indexes.cpp:342
bool deleteTokenOffer(ApplyView &view, std::shared_ptr< SLE > const &offer)
Deletes the given token offer.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:48
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
std::size_t constexpr maxDeletableDirEntries
The maximum number of owner directory entries for account to be deletable.
Definition Protocol.h:65
base_uint< 256 > uint256
Definition base_uint.h:558
static TER removeSignersFromLedger(Application &app, ApplyView &view, Keylet const &accountKeylet, Keylet const &ownerDirKeylet, Keylet const &signerListKeylet, beast::Journal j)
@ lsfRequireDestTag
@ lsfPasswordSpent
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE > const &sleDst, beast::Journal j)
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
Definition View.cpp:904
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< std::uint16_t > maxNodesToDelete=std::nullopt)
Cleanup owner directory entries on account delete.
@ tefBAD_LEDGER
Definition TER.h:170
@ tefTOO_BIG
Definition TER.h:184
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
@ tecNO_DST
Definition TER.h:290
@ tecTOO_SOON
Definition TER.h:318
@ tecNO_PERMISSION
Definition TER.h:305
@ tecDST_TAG_NEEDED
Definition TER.h:309
@ tecHAS_OBLIGATIONS
Definition TER.h:317
@ tesSUCCESS
Definition TER.h:244
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
bool cdirFirst(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the first entry in the directory, advancing the index.
Definition View.cpp:145
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
LedgerEntryType
Identifiers for on-ledger objects.
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:63
bool cdirNext(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the next entry in the directory, advancing the index.
Definition View.cpp:156
@ terNO_ACCOUNT
Definition TER.h:217
TERSubset< CanCvtToTER > TER
Definition TER.h:645
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1634
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
@ temINVALID_FLAG
Definition TER.h:111
@ temDISABLED
Definition TER.h:114
@ temDST_IS_SRC
Definition TER.h:108
XRPAmount increment
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:39
uint256 key
Definition Keylet.h:40
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:80
ReadView const & view
Definition Transactor.h:83
beast::Journal const j
Definition Transactor.h:88
State information when preflighting a tx.
Definition Transactor.h:35
beast::Journal const j
Definition Transactor.h:42