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);
104 }
105 
106 TER
107 removeDepositPreauthFromLedger(
108  Application& app,
109  ApplyView& view,
110  AccountID const& account,
111  uint256 const& delIndex,
112  std::shared_ptr<SLE> const& sleDel,
113  beast::Journal j)
114 {
115  return DepositPreauth::removeFromLedger(app, view, delIndex, j);
116 }
117 
118 // Return nullptr if the LedgerEntryType represents an obligation that can't be
119 // deleted Otherwise return the pointer to the function that can delete the
120 // non-obligation
121 DeleterFuncPtr
122 nonObligationDeleter(LedgerEntryType t)
123 {
124  switch (t)
125  {
126  case ltOFFER:
127  return offerDelete;
128  case ltSIGNER_LIST:
130  // case ltTICKET: return ???;
131  case ltDEPOSIT_PREAUTH:
132  return removeDepositPreauthFromLedger;
133  default:
134  return nullptr;
135  }
136 }
137 
138 } // namespace
139 
140 TER
142 {
143  AccountID const account{ctx.tx[sfAccount]};
144  AccountID const dst{ctx.tx[sfDestination]};
145 
146  auto sleDst = ctx.view.read(keylet::account(dst));
147 
148  if (!sleDst)
149  return tecNO_DST;
150 
151  if ((*sleDst)[sfFlags] & lsfRequireDestTag && !ctx.tx[~sfDestinationTag])
152  return tecDST_TAG_NEEDED;
153 
154  // Check whether the destination account requires deposit authorization.
155  if (ctx.view.rules().enabled(featureDepositAuth) &&
156  (sleDst->getFlags() & lsfDepositAuth))
157  {
158  if (!ctx.view.exists(keylet::depositPreauth(dst, account)))
159  return tecNO_PERMISSION;
160  }
161 
162  auto sleAccount = ctx.view.read(keylet::account(account));
163  assert(sleAccount);
164  if (!sleAccount)
165  return terNO_ACCOUNT;
166 
167  // We don't allow an account to be deleted if its sequence number
168  // is within 256 of the current ledger. This prevents replay of old
169  // transactions if this account is resurrected after it is deleted.
170  //
171  // We look at the account's Sequence rather than the transaction's
172  // Sequence in preparation for Tickets.
173  constexpr std::uint32_t seqDelta{255};
174  if ((*sleAccount)[sfSequence] + seqDelta > ctx.view.seq())
175  return tecTOO_SOON;
176 
177  // Verify that the account does not own any objects that would prevent
178  // the account from being deleted.
179  Keylet const ownerDirKeylet{keylet::ownerDir(account)};
180  if (dirIsEmpty(ctx.view, ownerDirKeylet))
181  return tesSUCCESS;
182 
183  std::shared_ptr<SLE const> sleDirNode{};
184  unsigned int uDirEntry{0};
185  uint256 dirEntry{beast::zero};
186 
187  if (!cdirFirst(
188  ctx.view,
189  ownerDirKeylet.key,
190  sleDirNode,
191  uDirEntry,
192  dirEntry,
193  ctx.j))
194  // Account has no directory at all. Looks good.
195  return tesSUCCESS;
196 
197  std::int32_t deletableDirEntryCount{0};
198  do
199  {
200  // Make sure any directory node types that we find are the kind
201  // we can delete.
202  Keylet const itemKeylet{ltCHILD, dirEntry};
203  auto sleItem = ctx.view.read(itemKeylet);
204  if (!sleItem)
205  {
206  // Directory node has an invalid index. Bail out.
207  JLOG(ctx.j.fatal())
208  << "DeleteAccount: directory node in ledger " << ctx.view.seq()
209  << " has index to object that is missing: "
210  << to_string(dirEntry);
211  return tefBAD_LEDGER;
212  }
213 
214  LedgerEntryType const nodeType{
215  safe_cast<LedgerEntryType>((*sleItem)[sfLedgerEntryType])};
216 
217  if (!nonObligationDeleter(nodeType))
218  return tecHAS_OBLIGATIONS;
219 
220  // We found a deletable directory entry. Count it. If we find too
221  // many deletable directory entries then bail out.
222  if (++deletableDirEntryCount > maxDeletableDirEntries)
223  return tefTOO_BIG;
224 
225  } while (cdirNext(
226  ctx.view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry, ctx.j));
227 
228  return tesSUCCESS;
229 }
230 
231 TER
233 {
234  auto src = view().peek(keylet::account(account_));
235  assert(src);
236 
237  auto dst = view().peek(keylet::account(ctx_.tx[sfDestination]));
238  assert(dst);
239 
240  if (!src || !dst)
241  return tefBAD_LEDGER;
242 
243  // Delete all of the entries in the account directory.
244  Keylet const ownerDirKeylet{keylet::ownerDir(account_)};
245  std::shared_ptr<SLE> sleDirNode{};
246  unsigned int uDirEntry{0};
247  uint256 dirEntry{beast::zero};
248 
249  if (view().exists(ownerDirKeylet) &&
250  dirFirst(
251  view(), ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry, j_))
252  {
253  do
254  {
255  // Choose the right way to delete each directory node.
256  Keylet const itemKeylet{ltCHILD, dirEntry};
257  auto sleItem = view().peek(itemKeylet);
258  if (!sleItem)
259  {
260  // Directory node has an invalid index. Bail out.
261  JLOG(j_.fatal())
262  << "DeleteAccount: Directory node in ledger "
263  << view().seq() << " has index to object that is missing: "
264  << to_string(dirEntry);
265  return tefBAD_LEDGER;
266  }
267 
268  LedgerEntryType const nodeType{safe_cast<LedgerEntryType>(
269  sleItem->getFieldU16(sfLedgerEntryType))};
270 
271  if (auto deleter = nonObligationDeleter(nodeType))
272  {
273  TER const result{
274  deleter(ctx_.app, view(), account_, dirEntry, sleItem, j_)};
275 
276  if (!isTesSuccess(result))
277  return result;
278  }
279  else
280  {
281  assert(!"Undeletable entry should be found in preclaim.");
282  JLOG(j_.error())
283  << "DeleteAccount undeletable item not found in preclaim.";
284  return tecHAS_OBLIGATIONS;
285  }
286 
287  // dirFirst() and dirNext() are like iterators with exposed
288  // internal state. We'll take advantage of that exposed state
289  // to solve a common C++ problem: iterator invalidation while
290  // deleting elements from a container.
291  //
292  // We have just deleted one directory entry, which means our
293  // "iterator state" is invalid.
294  //
295  // 1. During the process of getting an entry from the
296  // directory uDirEntry was incremented from 0 to 1.
297  //
298  // 2. We then deleted the entry at index 0, which means the
299  // entry that was at 1 has now moved to 0.
300  //
301  // 3. So we verify that uDirEntry is indeed 1. Then we jam it
302  // back to zero to "un-invalidate" the iterator.
303  assert(uDirEntry == 1);
304  if (uDirEntry != 1)
305  {
306  JLOG(j_.error())
307  << "DeleteAccount iterator re-validation failed.";
308  return tefBAD_LEDGER;
309  }
310  uDirEntry = 0;
311 
312  } while (dirNext(
313  view(), ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry, j_));
314  }
315 
316  // Transfer any XRP remaining after the fee is paid to the destination:
317  (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance;
318  (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance;
320 
321  assert((*src)[sfBalance] == XRPAmount(0));
322 
323  // If there's still an owner directory associated with the source account
324  // delete it.
325  if (view().exists(ownerDirKeylet) && !view().emptyDirDelete(ownerDirKeylet))
326  {
327  JLOG(j_.error()) << "DeleteAccount cannot delete root dir node of "
328  << toBase58(account_);
329  return tecHAS_OBLIGATIONS;
330  }
331 
332  // Re-arm the password change fee if we can and need to.
333  if (mSourceBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
334  dst->clearFlag(lsfPasswordSpent);
335 
336  view().update(dst);
337  view().erase(src);
338 
339  return tesSUCCESS;
340 }
341 
342 } // 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:276
ripple::Application
Definition: Application.h:94
ripple::preflight2
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:90
ripple::lsfPasswordSpent
@ lsfPasswordSpent
Definition: LedgerFormats.h:101
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::sfLedgerEntryType
const SF_U16 sfLedgerEntryType(access, STI_UINT16, 1, "LedgerEntryType", SField::sMD_Never)
Definition: SField.h:345
ripple::Transactor::j_
const beast::Journal j_
Definition: Transactor.h:90
ripple::isTesSuccess
bool isTesSuccess(TER x)
Definition: TER.h:576
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:495
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:355
ripple::removeSignersFromLedger
static TER removeSignersFromLedger(Application &app, ApplyView &view, Keylet const &accountKeylet, Keylet const &ownerDirKeylet, Keylet const &signerListKeylet)
Definition: SetSignerList.cpp:175
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:476
ripple::sfFlags
const SF_U32 sfFlags(access, STI_UINT32, 2, "Flags")
Definition: SField.h:353
ripple::featureDepositAuth
const uint256 featureDepositAuth
Definition: Feature.cpp:166
ripple::tecDST_TAG_NEEDED
@ tecDST_TAG_NEEDED
Definition: TER.h:267
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:232
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
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:141
ripple::temDST_IS_SRC
@ temDST_IS_SRC
Definition: TER.h:103
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:112
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:493
ripple::featureDeletableAccounts
const uint256 featureDeletableAccounts
Definition: Feature.cpp:183
ripple::sfDestinationTag
const SF_U32 sfDestinationTag(access, STI_UINT32, 14, "DestinationTag")
Definition: SField.h:365
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
Definition: base_uint.h:63
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:106
ripple::tefBAD_LEDGER
@ tefBAD_LEDGER
Definition: TER.h:146
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:47
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:120
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:547
ripple::DeleteAccount::maxDeletableDirEntries
static constexpr std::int32_t maxDeletableDirEntries
Definition: DeleteAccount.h:38
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::Transactor::calculateBaseFee
static FeeUnit64 calculateBaseFee(ReadView const &view, STTx const &tx)
Definition: Transactor.cpp:121
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:102
ripple::terNO_ACCOUNT
@ terNO_ACCOUNT
Definition: TER.h:190
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:276
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:188
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:440
ripple::Transactor::view
ApplyView & view()
Definition: Transactor.h:107
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:256
ripple::ReadView::rules
virtual Rules const & rules() const =0
Returns the tx processing rules.
ripple::tefTOO_BIG
@ tefTOO_BIG
Definition: TER.h:160
ripple::tecHAS_OBLIGATIONS
@ tecHAS_OBLIGATIONS
Definition: TER.h:275
ripple::tecNO_PERMISSION
@ tecNO_PERMISSION
Definition: TER.h:263
ripple::Transactor::mSourceBalance
XRPAmount mSourceBalance
Definition: Transactor.h:94
ripple::Transactor::ctx_
ApplyContext & ctx_
Definition: Transactor.h:89
ripple::Transactor::account_
AccountID account_
Definition: Transactor.h:92
ripple::sfDestination
const SF_Account sfDestination(access, STI_ACCOUNT, 3, "Destination")
Definition: SField.h:478
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:260
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:213
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:248
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::NotTEC
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:507
ripple::SetSignerList::removeFromLedger
static TER removeFromLedger(Application &app, ApplyView &view, AccountID const &account)
Definition: SetSignerList.cpp:221
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