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
41bool
43{
44 if (!ctx.rules.enabled(featureDeletableAccounts))
45 return false;
46
47 if (ctx.tx.isFieldPresent(sfCredentialIDs) &&
48 !ctx.rules.enabled(featureCredentials))
49 return false;
50
51 return true;
52}
53
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 tesSUCCESS;
66}
67
70{
71 // The fee required for AccountDelete is one owner reserve.
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; // LCOV_EXCL_LINE
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 an issuer has any issued NFTs resident in the ledger then it
269 // cannot be deleted.
270 if ((*sleAccount)[~sfMintedNFTokens] != (*sleAccount)[~sfBurnedNFTokens])
271 return tecHAS_OBLIGATIONS;
272
273 // If the account owns any NFTs it cannot be deleted.
274 Keylet const first = keylet::nftpage_min(account);
275 Keylet const last = keylet::nftpage_max(account);
276
277 auto const cp = ctx.view.read(Keylet(
278 ltNFTOKEN_PAGE,
279 ctx.view.succ(first.key, last.key.next()).value_or(last.key)));
280 if (cp)
281 return tecHAS_OBLIGATIONS;
282
283 // We don't allow an account to be deleted if its sequence number
284 // is within 256 of the current ledger. This prevents replay of old
285 // transactions if this account is resurrected after it is deleted.
286 //
287 // We look at the account's Sequence rather than the transaction's
288 // Sequence in preparation for Tickets.
289 constexpr std::uint32_t seqDelta{255};
290 if ((*sleAccount)[sfSequence] + seqDelta > ctx.view.seq())
291 return tecTOO_SOON;
292
293 // We don't allow an account to be deleted if
294 // <FirstNFTokenSequence + MintedNFTokens> is within 256 of the
295 // current ledger. This is to prevent having duplicate NFTokenIDs after
296 // account re-creation.
297 //
298 // Without this restriction, duplicate NFTokenIDs can be reproduced when
299 // authorized minting is involved. Because when the minter mints a NFToken,
300 // the issuer's sequence does not change. So when the issuer re-creates
301 // their account and mints a NFToken, it is possible that the
302 // NFTokenSequence of this NFToken is the same as the one that the
303 // authorized minter minted in a previous ledger.
304 if ((*sleAccount)[~sfFirstNFTokenSequence].value_or(0) +
305 (*sleAccount)[~sfMintedNFTokens].value_or(0) + seqDelta >
306 ctx.view.seq())
307 return tecTOO_SOON;
308
309 // Verify that the account does not own any objects that would prevent
310 // the account from being deleted.
311 Keylet const ownerDirKeylet{keylet::ownerDir(account)};
312 if (dirIsEmpty(ctx.view, ownerDirKeylet))
313 return tesSUCCESS;
314
315 std::shared_ptr<SLE const> sleDirNode{};
316 unsigned int uDirEntry{0};
317 uint256 dirEntry{beast::zero};
318
319 // Account has no directory at all. This _should_ have been caught
320 // by the dirIsEmpty() check earlier, but it's okay to catch it here.
321 if (!cdirFirst(
322 ctx.view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
323 return tesSUCCESS;
324
325 std::int32_t deletableDirEntryCount{0};
326 do
327 {
328 // Make sure any directory node types that we find are the kind
329 // we can delete.
330 auto sleItem = ctx.view.read(keylet::child(dirEntry));
331 if (!sleItem)
332 {
333 // Directory node has an invalid index. Bail out.
334 // LCOV_EXCL_START
335 JLOG(ctx.j.fatal())
336 << "DeleteAccount: directory node in ledger " << ctx.view.seq()
337 << " has index to object that is missing: "
338 << to_string(dirEntry);
339 return tefBAD_LEDGER;
340 // LCOV_EXCL_STOP
341 }
342
343 LedgerEntryType const nodeType{
344 safe_cast<LedgerEntryType>((*sleItem)[sfLedgerEntryType])};
345
346 if (!nonObligationDeleter(nodeType))
347 return tecHAS_OBLIGATIONS;
348
349 // We found a deletable directory entry. Count it. If we find too
350 // many deletable directory entries then bail out.
351 if (++deletableDirEntryCount > maxDeletableDirEntries)
352 return tefTOO_BIG;
353
354 } while (cdirNext(
355 ctx.view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
356
357 return tesSUCCESS;
358}
359
360TER
362{
363 auto src = view().peek(keylet::account(account_));
364 XRPL_ASSERT(
365 src, "ripple::DeleteAccount::doApply : non-null source account");
366
367 auto const dstID = ctx_.tx[sfDestination];
368 auto dst = view().peek(keylet::account(dstID));
369 XRPL_ASSERT(
370 dst, "ripple::DeleteAccount::doApply : non-null destination account");
371
372 if (!src || !dst)
373 return tefBAD_LEDGER; // LCOV_EXCL_LINE
374
375 if (ctx_.view().rules().enabled(featureDepositAuth) &&
376 ctx_.tx.isFieldPresent(sfCredentialIDs))
377 {
378 if (auto err = verifyDepositPreauth(
379 ctx_.tx, ctx_.view(), account_, dstID, dst, ctx_.journal);
380 !isTesSuccess(err))
381 return err;
382 }
383
384 Keylet const ownerDirKeylet{keylet::ownerDir(account_)};
385 auto const ter = cleanupOnAccountDelete(
386 view(),
387 ownerDirKeylet,
388 [&](LedgerEntryType nodeType,
389 uint256 const& dirEntry,
391 if (auto deleter = nonObligationDeleter(nodeType))
392 {
393 TER const result{
394 deleter(ctx_.app, view(), account_, dirEntry, sleItem, j_)};
395
396 return {result, SkipEntry::No};
397 }
398
399 // LCOV_EXCL_START
400 UNREACHABLE(
401 "ripple::DeleteAccount::doApply : undeletable item not found "
402 "in preclaim");
403 JLOG(j_.error()) << "DeleteAccount undeletable item not "
404 "found in preclaim.";
406 // LCOV_EXCL_STOP
407 },
408 ctx_.journal);
409 if (ter != tesSUCCESS)
410 return ter;
411
412 // Transfer any XRP remaining after the fee is paid to the destination:
413 (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance;
414 (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance;
416
417 XRPL_ASSERT(
418 (*src)[sfBalance] == XRPAmount(0),
419 "ripple::DeleteAccount::doApply : source balance is zero");
420
421 // If there's still an owner directory associated with the source account
422 // delete it.
423 if (view().exists(ownerDirKeylet) && !view().emptyDirDelete(ownerDirKeylet))
424 {
425 JLOG(j_.error()) << "DeleteAccount cannot delete root dir node of "
426 << toBase58(account_);
427 return tecHAS_OBLIGATIONS;
428 }
429
430 // Re-arm the password change fee if we can and need to.
431 if (mSourceBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
432 dst->clearFlag(lsfPasswordSpent);
433
434 view().update(dst);
435 view().erase(src);
436
437 return tesSUCCESS;
438}
439
440} // 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:172
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 bool checkExtraFeatures(PreflightContext 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 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
static TER removeFromLedger(Application &app, ApplyView &view, AccountID const &account, beast::Journal j)
AccountID const account_
Definition Transactor.h:147
ApplyView & view()
Definition Transactor.h:163
static XRPAmount calculateOwnerReserveFee(ReadView const &view, STTx const &tx)
beast::Journal const j_
Definition Transactor.h:145
static TER ticketDelete(ApplyView &view, AccountID const &account, uint256 const &ticketIndex, beast::Journal j)
XRPAmount mSourceBalance
Definition Transactor.h:149
ApplyContext & ctx_
Definition Transactor.h:143
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:68
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
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:907
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
@ tecNO_DST
Definition TER.h:291
@ tecTOO_SOON
Definition TER.h:319
@ tecNO_PERMISSION
Definition TER.h:306
@ tecDST_TAG_NEEDED
Definition TER.h:310
@ tecHAS_OBLIGATIONS
Definition TER.h:318
@ tesSUCCESS
Definition TER.h:245
bool isTesSuccess(TER x) noexcept
Definition TER.h:678
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.
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:649
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
@ temDST_IS_SRC
Definition TER.h:108
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