rippled
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 <ripple/app/tx/impl/DeleteAccount.h>
21 #include <ripple/app/tx/impl/DepositPreauth.h>
22 #include <ripple/app/tx/impl/SetSignerList.h>
23 #include <ripple/basics/FeeUnits.h>
24 #include <ripple/basics/Log.h>
25 #include <ripple/basics/mulDiv.h>
26 #include <ripple/ledger/View.h>
27 #include <ripple/protocol/Feature.h>
28 #include <ripple/protocol/Indexes.h>
29 #include <ripple/protocol/TxFlags.h>
30 #include <ripple/protocol/st.h>
31 
32 namespace ripple {
33 
34 NotTEC
36 {
38  return temDISABLED;
39 
40  if (ctx.tx.getFlags() & tfUniversalMask)
41  return temINVALID_FLAG;
42 
43  auto const ret = preflight1(ctx);
44 
45  if (!isTesSuccess(ret))
46  return ret;
47 
48  if (ctx.tx[sfAccount] == ctx.tx[sfDestination])
49  // An account cannot be deleted and give itself the resulting XRP.
50  return temDST_IS_SRC;
51 
52  return preflight2(ctx);
53 }
54 
57 {
58  // The fee required for AccountDelete is one owner reserve. But the
59  // owner reserve is stored in drops. We need to convert it to fee units.
60  Fees const& fees{view.fees()};
61  std::pair<bool, FeeUnit64> const mulDivResult{
62  mulDiv(fees.increment, safe_cast<FeeUnit64>(fees.units), fees.base)};
63  if (mulDivResult.first)
64  return mulDivResult.second;
65 
66  // If mulDiv returns false then overflow happened. Punt by using the
67  // standard calculation.
69 }
70 
71 namespace {
72 // Define a function pointer type that can be used to delete ledger node types.
73 using DeleterFuncPtr = TER (*)(
74  Application& app,
75  ApplyView& view,
76  AccountID const& account,
77  uint256 const& delIndex,
78  std::shared_ptr<SLE> const& sleDel,
79  beast::Journal j);
80 
81 // Local function definitions that provides signature compatibility.
82 TER
84  Application& app,
85  ApplyView& view,
86  AccountID const& account,
87  uint256 const& delIndex,
88  std::shared_ptr<SLE> const& sleDel,
90 {
91  return offerDelete(view, sleDel, j);
92 }
93 
94 TER
96  Application& app,
97  ApplyView& view,
98  AccountID const& account,
99  uint256 const& delIndex,
100  std::shared_ptr<SLE> const& sleDel,
101  beast::Journal j)
102 {
103  return SetSignerList::removeFromLedger(app, view, account, j);
104 }
105 
106 TER
107 removeTicketFromLedger(
108  Application&,
109  ApplyView& view,
110  AccountID const& account,
111  uint256 const& delIndex,
112  std::shared_ptr<SLE> const&,
113  beast::Journal j)
114 {
115  return Transactor::ticketDelete(view, account, delIndex, j);
116 }
117 
118 TER
119 removeDepositPreauthFromLedger(
120  Application& app,
121  ApplyView& view,
122  AccountID const& account,
123  uint256 const& delIndex,
124  std::shared_ptr<SLE> const& sleDel,
125  beast::Journal j)
126 {
127  return DepositPreauth::removeFromLedger(app, view, delIndex, j);
128 }
129 
130 // Return nullptr if the LedgerEntryType represents an obligation that can't
131 // be deleted. Otherwise return the pointer to the function that can delete
132 // the non-obligation
133 DeleterFuncPtr
134 nonObligationDeleter(LedgerEntryType t)
135 {
136  switch (t)
137  {
138  case ltOFFER:
139  return offerDelete;
140  case ltSIGNER_LIST:
142  case ltTICKET:
143  return removeTicketFromLedger;
144  case ltDEPOSIT_PREAUTH:
145  return removeDepositPreauthFromLedger;
146  default:
147  return nullptr;
148  }
149 }
150 
151 } // namespace
152 
153 TER
155 {
156  AccountID const account{ctx.tx[sfAccount]};
157  AccountID const dst{ctx.tx[sfDestination]};
158 
159  auto sleDst = ctx.view.read(keylet::account(dst));
160 
161  if (!sleDst)
162  return tecNO_DST;
163 
164  if ((*sleDst)[sfFlags] & lsfRequireDestTag && !ctx.tx[~sfDestinationTag])
165  return tecDST_TAG_NEEDED;
166 
167  // Check whether the destination account requires deposit authorization.
168  if (ctx.view.rules().enabled(featureDepositAuth) &&
169  (sleDst->getFlags() & lsfDepositAuth))
170  {
171  if (!ctx.view.exists(keylet::depositPreauth(dst, account)))
172  return tecNO_PERMISSION;
173  }
174 
175  auto sleAccount = ctx.view.read(keylet::account(account));
176  assert(sleAccount);
177  if (!sleAccount)
178  return terNO_ACCOUNT;
179 
180  // We don't allow an account to be deleted if its sequence number
181  // is within 256 of the current ledger. This prevents replay of old
182  // transactions if this account is resurrected after it is deleted.
183  //
184  // We look at the account's Sequence rather than the transaction's
185  // Sequence in preparation for Tickets.
186  constexpr std::uint32_t seqDelta{255};
187  if ((*sleAccount)[sfSequence] + seqDelta > ctx.view.seq())
188  return tecTOO_SOON;
189 
190  // Verify that the account does not own any objects that would prevent
191  // the account from being deleted.
192  Keylet const ownerDirKeylet{keylet::ownerDir(account)};
193  if (dirIsEmpty(ctx.view, ownerDirKeylet))
194  return tesSUCCESS;
195 
196  std::shared_ptr<SLE const> sleDirNode{};
197  unsigned int uDirEntry{0};
198  uint256 dirEntry{beast::zero};
199 
200  if (!cdirFirst(
201  ctx.view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
202  // Account has no directory at all. This _should_ have been caught
203  // by the dirIsEmpty() check earlier, but it's okay to catch it here.
204  return tesSUCCESS;
205 
206  std::int32_t deletableDirEntryCount{0};
207  do
208  {
209  // Make sure any directory node types that we find are the kind
210  // we can delete.
211  auto sleItem = ctx.view.read(keylet::child(dirEntry));
212  if (!sleItem)
213  {
214  // Directory node has an invalid index. Bail out.
215  JLOG(ctx.j.fatal())
216  << "DeleteAccount: directory node in ledger " << ctx.view.seq()
217  << " has index to object that is missing: "
218  << to_string(dirEntry);
219  return tefBAD_LEDGER;
220  }
221 
222  LedgerEntryType const nodeType{
223  safe_cast<LedgerEntryType>((*sleItem)[sfLedgerEntryType])};
224 
225  if (!nonObligationDeleter(nodeType))
226  return tecHAS_OBLIGATIONS;
227 
228  // We found a deletable directory entry. Count it. If we find too
229  // many deletable directory entries then bail out.
230  if (++deletableDirEntryCount > maxDeletableDirEntries)
231  return tefTOO_BIG;
232 
233  } while (cdirNext(
234  ctx.view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
235 
236  return tesSUCCESS;
237 }
238 
239 TER
241 {
242  auto src = view().peek(keylet::account(account_));
243  assert(src);
244 
245  auto dst = view().peek(keylet::account(ctx_.tx[sfDestination]));
246  assert(dst);
247 
248  if (!src || !dst)
249  return tefBAD_LEDGER;
250 
251  // Delete all of the entries in the account directory.
252  Keylet const ownerDirKeylet{keylet::ownerDir(account_)};
253  std::shared_ptr<SLE> sleDirNode{};
254  unsigned int uDirEntry{0};
255  uint256 dirEntry{beast::zero};
256 
257  if (view().exists(ownerDirKeylet) &&
258  dirFirst(view(), ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
259  {
260  do
261  {
262  // Choose the right way to delete each directory node.
263  auto sleItem = view().peek(keylet::child(dirEntry));
264  if (!sleItem)
265  {
266  // Directory node has an invalid index. Bail out.
267  JLOG(j_.fatal())
268  << "DeleteAccount: Directory node in ledger "
269  << view().seq() << " has index to object that is missing: "
270  << to_string(dirEntry);
271  return tefBAD_LEDGER;
272  }
273 
274  LedgerEntryType const nodeType{safe_cast<LedgerEntryType>(
275  sleItem->getFieldU16(sfLedgerEntryType))};
276 
277  if (auto deleter = nonObligationDeleter(nodeType))
278  {
279  TER const result{
280  deleter(ctx_.app, view(), account_, dirEntry, sleItem, j_)};
281 
282  if (!isTesSuccess(result))
283  return result;
284  }
285  else
286  {
287  assert(!"Undeletable entry should be found in preclaim.");
288  JLOG(j_.error())
289  << "DeleteAccount undeletable item not found in preclaim.";
290  return tecHAS_OBLIGATIONS;
291  }
292 
293  // dirFirst() and dirNext() are like iterators with exposed
294  // internal state. We'll take advantage of that exposed state
295  // to solve a common C++ problem: iterator invalidation while
296  // deleting elements from a container.
297  //
298  // We have just deleted one directory entry, which means our
299  // "iterator state" is invalid.
300  //
301  // 1. During the process of getting an entry from the
302  // directory uDirEntry was incremented from 0 to 1.
303  //
304  // 2. We then deleted the entry at index 0, which means the
305  // entry that was at 1 has now moved to 0.
306  //
307  // 3. So we verify that uDirEntry is indeed 1. Then we jam it
308  // back to zero to "un-invalidate" the iterator.
309  assert(uDirEntry == 1);
310  if (uDirEntry != 1)
311  {
312  JLOG(j_.error())
313  << "DeleteAccount iterator re-validation failed.";
314  return tefBAD_LEDGER;
315  }
316  uDirEntry = 0;
317 
318  } while (dirNext(
319  view(), ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
320  }
321 
322  // Transfer any XRP remaining after the fee is paid to the destination:
323  (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance;
324  (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance;
326 
327  assert((*src)[sfBalance] == XRPAmount(0));
328 
329  // If there's still an owner directory associated with the source account
330  // delete it.
331  if (view().exists(ownerDirKeylet) && !view().emptyDirDelete(ownerDirKeylet))
332  {
333  JLOG(j_.error()) << "DeleteAccount cannot delete root dir node of "
334  << toBase58(account_);
335  return tecHAS_OBLIGATIONS;
336  }
337 
338  // Re-arm the password change fee if we can and need to.
339  if (mSourceBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
340  dst->clearFlag(lsfPasswordSpent);
341 
342  view().update(dst);
343  view().erase(src);
344 
345  return tesSUCCESS;
346 }
347 
348 } // namespace ripple
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
ripple::keylet::ownerDir
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:300
ripple::Application
Definition: Application.h:115
ripple::cdirNext
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:147
ripple::dirNext
bool dirNext(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:125
ripple::preflight2
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:108
ripple::ltTICKET
@ ltTICKET
A ledger object which describes a ticket.
Definition: LedgerFormats.h:80
ripple::lsfPasswordSpent
@ lsfPasswordSpent
Definition: LedgerFormats.h:211
ripple::Keylet
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:38
std::shared_ptr
STL class.
ripple::PreclaimContext::view
ReadView const & view
Definition: Transactor.h:56
ripple::PreclaimContext::j
const beast::Journal j
Definition: Transactor.h:60
ripple::ApplyView::peek
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
ripple::sfDestination
const SF_ACCOUNT sfDestination
ripple::Transactor::j_
const beast::Journal j_
Definition: Transactor.h:89
ripple::isTesSuccess
bool isTesSuccess(TER x)
Definition: TER.h:584
std::pair
ripple::sfSequence
const SF_UINT32 sfSequence
ripple::ApplyView::erase
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
ripple::ReadView::fees
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
ripple::ltSIGNER_LIST
@ ltSIGNER_LIST
A ledger object which contains a signer list for an account.
Definition: LedgerFormats.h:86
ripple::featureDepositAuth
const uint256 featureDepositAuth
ripple::tecDST_TAG_NEEDED
@ tecDST_TAG_NEEDED
Definition: TER.h:271
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
ripple::DeleteAccount::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: DeleteAccount.cpp:35
ripple::keylet::child
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition: Indexes.cpp:136
ripple::ApplyView::update
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
ripple::DeleteAccount::doApply
TER doApply() override
Definition: DeleteAccount.cpp:240
ripple::DeleteAccount::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: DeleteAccount.cpp:154
ripple::temDST_IS_SRC
@ temDST_IS_SRC
Definition: TER.h:103
ripple::Transactor::ticketDelete
static TER ticketDelete(ApplyView &view, AccountID const &account, uint256 const &ticketIndex, beast::Journal j)
Definition: Transactor.cpp:361
ripple::preflight1
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:56
ripple::lsfDepositAuth
@ lsfDepositAuth
Definition: LedgerFormats.h:222
ripple::ApplyView
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:134
ripple::ApplyContext::app
Application & app
Definition: ApplyContext.h:47
ripple::uint256
base_uint< 256 > uint256
Definition: base_uint.h:529
ripple::featureDeletableAccounts
const uint256 featureDeletableAccounts
ripple::DeleteAccount::calculateBaseFee
static FeeUnit64 calculateBaseFee(ReadView const &view, STTx const &tx)
Definition: DeleteAccount.cpp:56
ripple::base_uint< 160, detail::AccountIDTag >
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:106
ripple::tefBAD_LEDGER
@ tefBAD_LEDGER
Definition: TER.h:148
ripple::DepositPreauth::removeFromLedger
static TER removeFromLedger(Application &app, ApplyView &view, uint256 const &delIndex, beast::Journal j)
Definition: DepositPreauth.cpp:167
ripple::ltOFFER
@ ltOFFER
A ledger object which describes an offer on the DEX.
Definition: LedgerFormats.h:92
ripple::Fees
Reflects the fee settings for a particular ledger.
Definition: ReadView.h:48
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:130
ripple::offerDelete
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition: View.cpp:893
ripple::TERSubset< CanCvtToTER >
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:555
ripple::DeleteAccount::maxDeletableDirEntries
static constexpr std::int32_t maxDeletableDirEntries
Definition: DeleteAccount.h:40
ripple::ltDEPOSIT_PREAUTH
@ ltDEPOSIT_PREAUTH
A ledger object which describes a deposit preauthorization.
Definition: LedgerFormats.h:142
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::ReadView::exists
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
ripple::STObject::getFlags
std::uint32_t getFlags() const
Definition: STObject.cpp:454
ripple::STTx
Definition: STTx.h:43
ripple::SetSignerList::removeFromLedger
static TER removeFromLedger(Application &app, ApplyView &view, AccountID const &account, beast::Journal j)
Definition: SetSignerList.cpp:223
ripple::Transactor::calculateBaseFee
static FeeUnit64 calculateBaseFee(ReadView const &view, STTx const &tx)
Definition: Transactor.cpp:140
ripple::ApplyContext::deliver
void deliver(STAmount const &amount)
Sets the DeliveredAmount field in the metadata.
Definition: ApplyContext.h:74
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint32_t
ripple::Rules::enabled
bool enabled(uint256 const &id) const
Returns true if a feature is enabled.
Definition: ReadView.cpp:102
ripple::feeunit::TaggedFee
Definition: FeeUnits.h:70
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple::PreclaimContext::tx
STTx const & tx
Definition: Transactor.h:58
ripple::lsfRequireDestTag
@ lsfRequireDestTag
Definition: LedgerFormats.h:212
ripple::terNO_ACCOUNT
@ terNO_ACCOUNT
Definition: TER.h:193
ripple::PreclaimContext
State information when determining if a tx is likely to claim a fee.
Definition: Transactor.h:52
ripple::tecTOO_SOON
@ tecTOO_SOON
Definition: TER.h:280
ripple::dirIsEmpty
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
Definition: View.cpp:590
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:192
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::dirFirst
bool dirFirst(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:114
ripple::sfLedgerEntryType
const SF_UINT16 sfLedgerEntryType
ripple::LedgerEntryType
LedgerEntryType
Identifiers for on-ledger objects.
Definition: LedgerFormats.h:53
ripple::Transactor::view
ApplyView & view()
Definition: Transactor.h:107
ripple::temDISABLED
@ temDISABLED
Definition: TER.h:109
ripple::ReadView::seq
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition: ReadView.h:260
ripple::ReadView::rules
virtual Rules const & rules() const =0
Returns the tx processing rules.
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::sfDestinationTag
const SF_UINT32 sfDestinationTag
ripple::tefTOO_BIG
@ tefTOO_BIG
Definition: TER.h:162
ripple::tecHAS_OBLIGATIONS
@ tecHAS_OBLIGATIONS
Definition: TER.h:279
ripple::tecNO_PERMISSION
@ tecNO_PERMISSION
Definition: TER.h:267
ripple::Transactor::mSourceBalance
XRPAmount mSourceBalance
Definition: Transactor.h:93
ripple::sfBalance
const SF_AMOUNT sfBalance
ripple::removeSignersFromLedger
static TER removeSignersFromLedger(Application &app, ApplyView &view, Keylet const &accountKeylet, Keylet const &ownerDirKeylet, Keylet const &signerListKeylet, beast::Journal j)
Definition: SetSignerList.cpp:175
ripple::Transactor::ctx_
ApplyContext & ctx_
Definition: Transactor.h:88
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:38
ripple::sfAccount
const SF_ACCOUNT sfAccount
ripple::mulDiv
std::pair< bool, Dest > mulDiv(Source1 value, Dest mul, Source2 div)
Definition: FeeUnits.h:473
ripple::tfUniversalMask
const std::uint32_t tfUniversalMask
Definition: TxFlags.h:50
ripple::PreflightContext::tx
STTx const & tx
Definition: Transactor.h:35
ripple::PreflightContext
State information when preflighting a tx.
Definition: Transactor.h:31
ripple::PreflightContext::rules
const Rules rules
Definition: Transactor.h:36
ripple::cdirFirst
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:136
ripple::keylet::depositPreauth
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition: Indexes.cpp:284
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:217
ripple::Transactor::account_
const AccountID account_
Definition: Transactor.h:91
ripple::ApplyContext::tx
STTx const & tx
Definition: ApplyContext.h:48
ripple::AccountID
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition: AccountID.h:47
ripple::tecNO_DST
@ tecNO_DST
Definition: TER.h:252
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::NotTEC
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:515