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