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/app/tx/impl/details/NFTokenUtils.h>
24 #include <ripple/basics/FeeUnits.h>
25 #include <ripple/basics/Log.h>
26 #include <ripple/basics/mulDiv.h>
27 #include <ripple/ledger/View.h>
28 #include <ripple/protocol/Feature.h>
29 #include <ripple/protocol/Indexes.h>
30 #include <ripple/protocol/Protocol.h>
31 #include <ripple/protocol/TxFlags.h>
32 #include <ripple/protocol/st.h>
33 
34 namespace ripple {
35 
36 NotTEC
38 {
40  return temDISABLED;
41 
42  if (ctx.tx.getFlags() & tfUniversalMask)
43  return temINVALID_FLAG;
44 
45  if (auto const ret = preflight1(ctx); !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.
59  return view.fees().increment;
60 }
61 
62 namespace {
63 // Define a function pointer type that can be used to delete ledger node types.
64 using DeleterFuncPtr = TER (*)(
65  Application& app,
66  ApplyView& view,
67  AccountID const& account,
68  uint256 const& delIndex,
69  std::shared_ptr<SLE> const& sleDel,
70  beast::Journal j);
71 
72 // Local function definitions that provides signature compatibility.
73 TER
75  Application& app,
76  ApplyView& view,
77  AccountID const& account,
78  uint256 const& delIndex,
79  std::shared_ptr<SLE> const& sleDel,
81 {
82  return offerDelete(view, sleDel, j);
83 }
84 
85 TER
87  Application& app,
88  ApplyView& view,
89  AccountID const& account,
90  uint256 const& delIndex,
91  std::shared_ptr<SLE> const& sleDel,
93 {
94  return SetSignerList::removeFromLedger(app, view, account, j);
95 }
96 
97 TER
98 removeTicketFromLedger(
99  Application&,
100  ApplyView& view,
101  AccountID const& account,
102  uint256 const& delIndex,
103  std::shared_ptr<SLE> const&,
104  beast::Journal j)
105 {
106  return Transactor::ticketDelete(view, account, delIndex, j);
107 }
108 
109 TER
110 removeDepositPreauthFromLedger(
111  Application& app,
112  ApplyView& view,
113  AccountID const& account,
114  uint256 const& delIndex,
115  std::shared_ptr<SLE> const& sleDel,
116  beast::Journal j)
117 {
118  return DepositPreauth::removeFromLedger(app, view, delIndex, j);
119 }
120 
121 TER
122 removeNFTokenOfferFromLedger(
123  Application& app,
124  ApplyView& view,
125  AccountID const& account,
126  uint256 const& delIndex,
127  std::shared_ptr<SLE> const& sleDel,
129 {
130  if (!nft::deleteTokenOffer(view, sleDel))
131  return tefBAD_LEDGER;
132 
133  return tesSUCCESS;
134 }
135 
136 // Return nullptr if the LedgerEntryType represents an obligation that can't
137 // be deleted. Otherwise return the pointer to the function that can delete
138 // the non-obligation
139 DeleterFuncPtr
140 nonObligationDeleter(LedgerEntryType t)
141 {
142  switch (t)
143  {
144  case ltOFFER:
145  return offerDelete;
146  case ltSIGNER_LIST:
148  case ltTICKET:
149  return removeTicketFromLedger;
150  case ltDEPOSIT_PREAUTH:
151  return removeDepositPreauthFromLedger;
152  case ltNFTOKEN_OFFER:
153  return removeNFTokenOfferFromLedger;
154  default:
155  return nullptr;
156  }
157 }
158 
159 } // namespace
160 
161 TER
163 {
164  AccountID const account{ctx.tx[sfAccount]};
165  AccountID const dst{ctx.tx[sfDestination]};
166 
167  auto sleDst = ctx.view.read(keylet::account(dst));
168 
169  if (!sleDst)
170  return tecNO_DST;
171 
172  if ((*sleDst)[sfFlags] & lsfRequireDestTag && !ctx.tx[~sfDestinationTag])
173  return tecDST_TAG_NEEDED;
174 
175  // Check whether the destination account requires deposit authorization.
176  if (ctx.view.rules().enabled(featureDepositAuth) &&
177  (sleDst->getFlags() & lsfDepositAuth))
178  {
179  if (!ctx.view.exists(keylet::depositPreauth(dst, account)))
180  return tecNO_PERMISSION;
181  }
182 
183  auto sleAccount = ctx.view.read(keylet::account(account));
184  assert(sleAccount);
185  if (!sleAccount)
186  return terNO_ACCOUNT;
187 
189  {
190  // If an issuer has any issued NFTs resident in the ledger then it
191  // cannot be deleted.
192  if ((*sleAccount)[~sfMintedNFTokens] !=
193  (*sleAccount)[~sfBurnedNFTokens])
194  return tecHAS_OBLIGATIONS;
195 
196  // If the account owns any NFTs it cannot be deleted.
197  Keylet const first = keylet::nftpage_min(account);
198  Keylet const last = keylet::nftpage_max(account);
199 
200  auto const cp = ctx.view.read(Keylet(
202  ctx.view.succ(first.key, last.key.next()).value_or(last.key)));
203  if (cp)
204  return tecHAS_OBLIGATIONS;
205  }
206 
207  // We don't allow an account to be deleted if its sequence number
208  // is within 256 of the current ledger. This prevents replay of old
209  // transactions if this account is resurrected after it is deleted.
210  //
211  // We look at the account's Sequence rather than the transaction's
212  // Sequence in preparation for Tickets.
213  constexpr std::uint32_t seqDelta{255};
214  if ((*sleAccount)[sfSequence] + seqDelta > ctx.view.seq())
215  return tecTOO_SOON;
216 
217  // When fixNFTokenRemint is enabled, we don't allow an account to be
218  // deleted if <FirstNFTokenSequence + MintedNFTokens> is within 256 of the
219  // current ledger. This is to prevent having duplicate NFTokenIDs after
220  // account re-creation.
221  //
222  // Without this restriction, duplicate NFTokenIDs can be reproduced when
223  // authorized minting is involved. Because when the minter mints a NFToken,
224  // the issuer's sequence does not change. So when the issuer re-creates
225  // their account and mints a NFToken, it is possible that the
226  // NFTokenSequence of this NFToken is the same as the one that the
227  // authorized minter minted in a previous ledger.
228  if (ctx.view.rules().enabled(fixNFTokenRemint) &&
229  ((*sleAccount)[~sfFirstNFTokenSequence].value_or(0) +
230  (*sleAccount)[~sfMintedNFTokens].value_or(0) + seqDelta >
231  ctx.view.seq()))
232  return tecTOO_SOON;
233 
234  // Verify that the account does not own any objects that would prevent
235  // the account from being deleted.
236  Keylet const ownerDirKeylet{keylet::ownerDir(account)};
237  if (dirIsEmpty(ctx.view, ownerDirKeylet))
238  return tesSUCCESS;
239 
240  std::shared_ptr<SLE const> sleDirNode{};
241  unsigned int uDirEntry{0};
242  uint256 dirEntry{beast::zero};
243 
244  // Account has no directory at all. This _should_ have been caught
245  // by the dirIsEmpty() check earlier, but it's okay to catch it here.
246  if (!cdirFirst(
247  ctx.view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
248  return tesSUCCESS;
249 
250  std::int32_t deletableDirEntryCount{0};
251  do
252  {
253  // Make sure any directory node types that we find are the kind
254  // we can delete.
255  auto sleItem = ctx.view.read(keylet::child(dirEntry));
256  if (!sleItem)
257  {
258  // Directory node has an invalid index. Bail out.
259  JLOG(ctx.j.fatal())
260  << "DeleteAccount: directory node in ledger " << ctx.view.seq()
261  << " has index to object that is missing: "
262  << to_string(dirEntry);
263  return tefBAD_LEDGER;
264  }
265 
266  LedgerEntryType const nodeType{
267  safe_cast<LedgerEntryType>((*sleItem)[sfLedgerEntryType])};
268 
269  if (!nonObligationDeleter(nodeType))
270  return tecHAS_OBLIGATIONS;
271 
272  // We found a deletable directory entry. Count it. If we find too
273  // many deletable directory entries then bail out.
274  if (++deletableDirEntryCount > maxDeletableDirEntries)
275  return tefTOO_BIG;
276 
277  } while (cdirNext(
278  ctx.view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
279 
280  return tesSUCCESS;
281 }
282 
283 TER
285 {
286  auto src = view().peek(keylet::account(account_));
287  assert(src);
288 
289  auto dst = view().peek(keylet::account(ctx_.tx[sfDestination]));
290  assert(dst);
291 
292  if (!src || !dst)
293  return tefBAD_LEDGER;
294 
295  Keylet const ownerDirKeylet{keylet::ownerDir(account_)};
296  auto const ter = cleanupOnAccountDelete(
297  view(),
298  ownerDirKeylet,
299  [&](LedgerEntryType nodeType,
300  uint256 const& dirEntry,
302  if (auto deleter = nonObligationDeleter(nodeType))
303  {
304  TER const result{
305  deleter(ctx_.app, view(), account_, dirEntry, sleItem, j_)};
306 
307  return {result, SkipEntry::No};
308  }
309 
310  assert(!"Undeletable entry should be found in preclaim.");
311  JLOG(j_.error()) << "DeleteAccount undeletable item not "
312  "found in preclaim.";
314  },
315  ctx_.journal);
316  if (ter != tesSUCCESS)
317  return ter;
318 
319  // Transfer any XRP remaining after the fee is paid to the destination:
320  (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance;
321  (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance;
323 
324  assert((*src)[sfBalance] == XRPAmount(0));
325 
326  // If there's still an owner directory associated with the source account
327  // delete it.
328  if (view().exists(ownerDirKeylet) && !view().emptyDirDelete(ownerDirKeylet))
329  {
330  JLOG(j_.error()) << "DeleteAccount cannot delete root dir node of "
331  << toBase58(account_);
332  return tecHAS_OBLIGATIONS;
333  }
334 
335  // Re-arm the password change fee if we can and need to.
336  if (mSourceBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
337  dst->clearFlag(lsfPasswordSpent);
338 
339  view().update(dst);
340  view().erase(src);
341 
342  return tesSUCCESS;
343 }
344 
345 } // 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:116
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:145
ripple::sfFirstNFTokenSequence
const SF_UINT32 sfFirstNFTokenSequence
ripple::preflight2
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:130
ripple::ltTICKET
@ ltTICKET
A ledger object which describes a ticket.
Definition: LedgerFormats.h:80
ripple::lsfPasswordSpent
@ lsfPasswordSpent
Definition: LedgerFormats.h:229
ripple::Keylet
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:38
ripple::Rules::enabled
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:94
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:608
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::sfMintedNFTokens
const SF_UINT32 sfMintedNFTokens
ripple::featureDepositAuth
const uint256 featureDepositAuth
ripple::tecDST_TAG_NEEDED
@ tecDST_TAG_NEEDED
Definition: TER.h:279
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:104
ripple::DeleteAccount::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: DeleteAccount.cpp:37
ripple::keylet::child
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition: Indexes.cpp:140
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:284
ripple::ApplyContext::journal
const beast::Journal journal
Definition: ApplyContext.h:51
ripple::base_uint::next
base_uint next() const
Definition: base_uint.h:448
ripple::DeleteAccount::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: DeleteAccount.cpp:162
ripple::temDST_IS_SRC
@ temDST_IS_SRC
Definition: TER.h:106
ripple::Transactor::ticketDelete
static TER ticketDelete(ApplyView &view, AccountID const &account, uint256 const &ticketIndex, beast::Journal j)
Definition: Transactor.cpp:383
ripple::preflight1
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:78
ripple::lsfDepositAuth
@ lsfDepositAuth
Definition: LedgerFormats.h:240
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::fixNFTokenRemint
const uint256 fixNFTokenRemint
ripple::uint256
base_uint< 256 > uint256
Definition: base_uint.h:550
ripple::featureDeletableAccounts
const uint256 featureDeletableAccounts
ripple::Fees::increment
XRPAmount increment
Definition: protocol/Fees.h:36
ripple::Keylet::key
uint256 key
Definition: Keylet.h:40
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:82
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:109
ripple::keylet::nftpage_min
Keylet nftpage_min(AccountID const &owner)
NFT page keylets.
Definition: Indexes.cpp:333
ripple::tefBAD_LEDGER
@ tefBAD_LEDGER
Definition: TER.h:154
ripple::DepositPreauth::removeFromLedger
static TER removeFromLedger(Application &app, ApplyView &view, uint256 const &delIndex, beast::Journal j)
Definition: DepositPreauth.cpp:166
ripple::ltOFFER
@ ltOFFER
A ledger object which describes an offer on the DEX.
Definition: LedgerFormats.h:92
ripple::maxDeletableDirEntries
constexpr std::size_t maxDeletableDirEntries
The maximum number of owner directory entries for account to be deletable.
Definition: Protocol.h:64
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:910
ripple::ltNFTOKEN_OFFER
@ ltNFTOKEN_OFFER
A ledger object which identifies an offer to buy or sell an NFT.
Definition: LedgerFormats.h:162
ripple::TERSubset< CanCvtToTER >
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:579
ripple::ltDEPOSIT_PREAUTH
@ ltDEPOSIT_PREAUTH
A ledger object which describes a deposit preauthorization.
Definition: LedgerFormats.h:142
ripple::keylet::nftpage_max
Keylet nftpage_max(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Definition: Indexes.cpp:341
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:481
ripple::STTx
Definition: STTx.h:45
ripple::SetSignerList::removeFromLedger
static TER removeFromLedger(Application &app, ApplyView &view, AccountID const &account, beast::Journal j)
Definition: SetSignerList.cpp:229
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::ReadView::succ
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.
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple::cleanupOnAccountDelete
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< uint16_t > maxNodesToDelete)
Definition: View.cpp:1531
ripple::PreclaimContext::tx
STTx const & tx
Definition: Transactor.h:58
ripple::lsfRequireDestTag
@ lsfRequireDestTag
Definition: LedgerFormats.h:230
ripple::terNO_ACCOUNT
@ terNO_ACCOUNT
Definition: TER.h:200
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:288
ripple::dirIsEmpty
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
Definition: View.cpp:607
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:54
ripple::ltNFTOKEN_PAGE
@ ltNFTOKEN_PAGE
A ledger object which contains a list of NFTs.
Definition: LedgerFormats.h:156
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::featureNonFungibleTokensV1
const uint256 featureNonFungibleTokensV1
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:112
ripple::ReadView::seq
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition: ReadView.h:122
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:168
ripple::tecHAS_OBLIGATIONS
@ tecHAS_OBLIGATIONS
Definition: TER.h:287
ripple::tecNO_PERMISSION
@ tecNO_PERMISSION
Definition: TER.h:275
ripple::Transactor::mSourceBalance
XRPAmount mSourceBalance
Definition: Transactor.h:93
ripple::SkipEntry::No
@ No
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:180
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:41
ripple::sfAccount
const SF_ACCOUNT sfAccount
ripple::PreflightContext::tx
STTx const & tx
Definition: Transactor.h:35
ripple::PreflightContext
State information when preflighting a tx.
Definition: Transactor.h:31
ripple::sfBurnedNFTokens
const SF_UINT32 sfBurnedNFTokens
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:134
ripple::tfUniversalMask
constexpr std::uint32_t tfUniversalMask
Definition: TxFlags.h:60
ripple::keylet::depositPreauth
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition: Indexes.cpp:288
ripple::DeleteAccount::calculateBaseFee
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Definition: DeleteAccount.cpp:56
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:225
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::nft::deleteTokenOffer
bool deleteTokenOffer(ApplyView &view, std::shared_ptr< SLE > const &offer)
Deletes the given token offer.
Definition: NFTokenUtils.cpp:605
ripple::tecNO_DST
@ tecNO_DST
Definition: TER.h:260
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::NotTEC
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:539