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,
202  ownerDirKeylet.key,
203  sleDirNode,
204  uDirEntry,
205  dirEntry,
206  ctx.j))
207  // Account has no directory at all. Looks good.
208  return tesSUCCESS;
209 
210  std::int32_t deletableDirEntryCount{0};
211  do
212  {
213  // Make sure any directory node types that we find are the kind
214  // we can delete.
215  Keylet const itemKeylet{ltCHILD, dirEntry};
216  auto sleItem = ctx.view.read(itemKeylet);
217  if (!sleItem)
218  {
219  // Directory node has an invalid index. Bail out.
220  JLOG(ctx.j.fatal())
221  << "DeleteAccount: directory node in ledger " << ctx.view.seq()
222  << " has index to object that is missing: "
223  << to_string(dirEntry);
224  return tefBAD_LEDGER;
225  }
226 
227  LedgerEntryType const nodeType{
228  safe_cast<LedgerEntryType>((*sleItem)[sfLedgerEntryType])};
229 
230  if (!nonObligationDeleter(nodeType))
231  return tecHAS_OBLIGATIONS;
232 
233  // We found a deletable directory entry. Count it. If we find too
234  // many deletable directory entries then bail out.
235  if (++deletableDirEntryCount > maxDeletableDirEntries)
236  return tefTOO_BIG;
237 
238  } while (cdirNext(
239  ctx.view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry, ctx.j));
240 
241  return tesSUCCESS;
242 }
243 
244 TER
246 {
247  auto src = view().peek(keylet::account(account_));
248  assert(src);
249 
250  auto dst = view().peek(keylet::account(ctx_.tx[sfDestination]));
251  assert(dst);
252 
253  if (!src || !dst)
254  return tefBAD_LEDGER;
255 
256  // Delete all of the entries in the account directory.
257  Keylet const ownerDirKeylet{keylet::ownerDir(account_)};
258  std::shared_ptr<SLE> sleDirNode{};
259  unsigned int uDirEntry{0};
260  uint256 dirEntry{beast::zero};
261 
262  if (view().exists(ownerDirKeylet) &&
263  dirFirst(
264  view(), ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry, j_))
265  {
266  do
267  {
268  // Choose the right way to delete each directory node.
269  Keylet const itemKeylet{ltCHILD, dirEntry};
270  auto sleItem = view().peek(itemKeylet);
271  if (!sleItem)
272  {
273  // Directory node has an invalid index. Bail out.
274  JLOG(j_.fatal())
275  << "DeleteAccount: Directory node in ledger "
276  << view().seq() << " has index to object that is missing: "
277  << to_string(dirEntry);
278  return tefBAD_LEDGER;
279  }
280 
281  LedgerEntryType const nodeType{safe_cast<LedgerEntryType>(
282  sleItem->getFieldU16(sfLedgerEntryType))};
283 
284  if (auto deleter = nonObligationDeleter(nodeType))
285  {
286  TER const result{
287  deleter(ctx_.app, view(), account_, dirEntry, sleItem, j_)};
288 
289  if (!isTesSuccess(result))
290  return result;
291  }
292  else
293  {
294  assert(!"Undeletable entry should be found in preclaim.");
295  JLOG(j_.error())
296  << "DeleteAccount undeletable item not found in preclaim.";
297  return tecHAS_OBLIGATIONS;
298  }
299 
300  // dirFirst() and dirNext() are like iterators with exposed
301  // internal state. We'll take advantage of that exposed state
302  // to solve a common C++ problem: iterator invalidation while
303  // deleting elements from a container.
304  //
305  // We have just deleted one directory entry, which means our
306  // "iterator state" is invalid.
307  //
308  // 1. During the process of getting an entry from the
309  // directory uDirEntry was incremented from 0 to 1.
310  //
311  // 2. We then deleted the entry at index 0, which means the
312  // entry that was at 1 has now moved to 0.
313  //
314  // 3. So we verify that uDirEntry is indeed 1. Then we jam it
315  // back to zero to "un-invalidate" the iterator.
316  assert(uDirEntry == 1);
317  if (uDirEntry != 1)
318  {
319  JLOG(j_.error())
320  << "DeleteAccount iterator re-validation failed.";
321  return tefBAD_LEDGER;
322  }
323  uDirEntry = 0;
324 
325  } while (dirNext(
326  view(), ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry, j_));
327  }
328 
329  // Transfer any XRP remaining after the fee is paid to the destination:
330  (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance;
331  (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance;
333 
334  assert((*src)[sfBalance] == XRPAmount(0));
335 
336  // If there's still an owner directory associated with the source account
337  // delete it.
338  if (view().exists(ownerDirKeylet) && !view().emptyDirDelete(ownerDirKeylet))
339  {
340  JLOG(j_.error()) << "DeleteAccount cannot delete root dir node of "
341  << toBase58(account_);
342  return tecHAS_OBLIGATIONS;
343  }
344 
345  // Re-arm the password change fee if we can and need to.
346  if (mSourceBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
347  dst->clearFlag(lsfPasswordSpent);
348 
349  view().update(dst);
350  view().erase(src);
351 
352  return tesSUCCESS;
353 }
354 
355 } // 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:304
ripple::Application
Definition: Application.h:97
ripple::preflight2
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:108
ripple::lsfPasswordSpent
@ lsfPasswordSpent
Definition: LedgerFormats.h:103
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:57
ripple::PreclaimContext::j
const beast::Journal j
Definition: Transactor.h:61
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:90
ripple::isTesSuccess
bool isTesSuccess(TER x)
Definition: TER.h:580
std::pair
ripple::sfSequence
const SF_UINT32 sfSequence
ripple::cdirNext
bool cdirNext(ReadView const &view, uint256 const &uRootIndex, std::shared_ptr< SLE const > &sleNode, unsigned int &uDirEntry, uint256 &uEntryIndex, beast::Journal j)
Definition: View.cpp:495
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::featureDepositAuth
const uint256 featureDepositAuth
Definition: Feature.cpp:169
ripple::tecDST_TAG_NEEDED
@ tecDST_TAG_NEEDED
Definition: TER.h:270
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::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:245
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:42
ripple::ltSIGNER_LIST
@ ltSIGNER_LIST
Definition: LedgerFormats.h:70
ripple::dirNext
bool dirNext(ApplyView &view, uint256 const &uRootIndex, std::shared_ptr< SLE > &sleNode, unsigned int &uDirEntry, uint256 &uEntryIndex, beast::Journal j)
Definition: View.cpp:675
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:346
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:114
ripple::ltTICKET
@ ltTICKET
Definition: LedgerFormats.h:68
ripple::ApplyView
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:140
ripple::ApplyContext::app
Application & app
Definition: ApplyContext.h:47
ripple::ltCHILD
@ ltCHILD
Special type, anything not a directory This is used when the type in the Keylet is unknown,...
Definition: LedgerFormats.h:47
ripple::uint256
base_uint< 256 > uint256
Definition: base_uint.h:457
ripple::featureDeletableAccounts
const uint256 featureDeletableAccounts
Definition: Feature.cpp:182
ripple::DeleteAccount::calculateBaseFee
static FeeUnit64 calculateBaseFee(ReadView const &view, STTx const &tx)
Definition: DeleteAccount.cpp:56
ripple::dirFirst
bool dirFirst(ApplyView &view, uint256 const &uRootIndex, std::shared_ptr< SLE > &sleNode, unsigned int &uDirEntry, uint256 &uEntryIndex, beast::Journal j)
Definition: View.cpp:660
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:73
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:106
ripple::tefBAD_LEDGER
@ tefBAD_LEDGER
Definition: TER.h:147
ripple::DepositPreauth::removeFromLedger
static TER removeFromLedger(Application &app, ApplyView &view, uint256 const &delIndex, beast::Journal j)
Definition: DepositPreauth.cpp:167
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:134
ripple::offerDelete
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition: View.cpp:896
ripple::TERSubset< CanCvtToTER >
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:551
ripple::DeleteAccount::maxDeletableDirEntries
static constexpr std::int32_t maxDeletableDirEntries
Definition: DeleteAccount.h:40
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:42
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:103
ripple::feeunit::TaggedFee
Definition: FeeUnits.h:70
ripple::ltDEPOSIT_PREAUTH
@ ltDEPOSIT_PREAUTH
Definition: LedgerFormats.h:87
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:59
ripple::lsfRequireDestTag
@ lsfRequireDestTag
Definition: LedgerFormats.h:104
ripple::terNO_ACCOUNT
@ terNO_ACCOUNT
Definition: TER.h:192
ripple::PreclaimContext
State information when determining if a tx is likely to claim a fee.
Definition: Transactor.h:53
ripple::tecTOO_SOON
@ tecTOO_SOON
Definition: TER.h:279
ripple::dirIsEmpty
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
Definition: View.cpp:466
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::sfLedgerEntryType
const SF_UINT16 sfLedgerEntryType
ripple::Transactor::view
ApplyView & view()
Definition: Transactor.h:108
ripple::temDISABLED
@ temDISABLED
Definition: TER.h:109
ripple::LedgerEntryType
LedgerEntryType
Ledger entry types.
Definition: LedgerFormats.h:36
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:161
ripple::tecHAS_OBLIGATIONS
@ tecHAS_OBLIGATIONS
Definition: TER.h:278
ripple::tecNO_PERMISSION
@ tecNO_PERMISSION
Definition: TER.h:266
ripple::Transactor::mSourceBalance
XRPAmount mSourceBalance
Definition: Transactor.h:94
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:89
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::ltOFFER
@ ltOFFER
Definition: LedgerFormats.h:72
ripple::PreflightContext::tx
STTx const & tx
Definition: Transactor.h:36
ripple::PreflightContext
State information when preflighting a tx.
Definition: Transactor.h:32
ripple::PreflightContext::rules
const Rules rules
Definition: Transactor.h:37
ripple::keylet::depositPreauth
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition: Indexes.cpp:288
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:216
ripple::Transactor::account_
const AccountID account_
Definition: Transactor.h:92
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:251
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::NotTEC
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:511
ripple::cdirFirst
bool cdirFirst(ReadView const &view, uint256 const &uRootIndex, std::shared_ptr< SLE const > &sleNode, unsigned int &uDirEntry, uint256 &uEntryIndex, beast::Journal j)
Definition: View.cpp:480