rippled
Loading...
Searching...
No Matches
View.cpp
1#include <xrpl/basics/Expected.h>
2#include <xrpl/basics/Log.h>
3#include <xrpl/basics/chrono.h>
4#include <xrpl/beast/utility/instrumentation.h>
5#include <xrpl/ledger/ReadView.h>
6#include <xrpl/ledger/View.h>
7#include <xrpl/ledger/helpers/AccountRootHelpers.h>
8#include <xrpl/ledger/helpers/CredentialHelpers.h>
9#include <xrpl/ledger/helpers/DirectoryHelpers.h>
10#include <xrpl/ledger/helpers/RippleStateHelpers.h>
11#include <xrpl/protocol/Feature.h>
12#include <xrpl/protocol/Indexes.h>
13#include <xrpl/protocol/LedgerFormats.h>
14#include <xrpl/protocol/MPTIssue.h>
15#include <xrpl/protocol/Protocol.h>
16#include <xrpl/protocol/Quality.h>
17#include <xrpl/protocol/TER.h>
18#include <xrpl/protocol/TxFlags.h>
19#include <xrpl/protocol/digest.h>
20#include <xrpl/protocol/st.h>
21
22#include <type_traits>
23#include <variant>
24
25namespace xrpl {
26
27//------------------------------------------------------------------------------
28//
29// Observers
30//
31//------------------------------------------------------------------------------
32
33bool
35{
36 using d = NetClock::duration;
37 using tp = NetClock::time_point;
38
39 return exp && (view.parentCloseTime() >= tp{d{*exp}});
40}
41
42bool
44 ReadView const& view,
45 AccountID const& account,
46 MPTIssue const& mptShare,
47 int depth)
48{
49 if (!view.rules().enabled(featureSingleAssetVault))
50 return false;
51
52 if (depth >= maxAssetCheckDepth)
53 return true; // LCOV_EXCL_LINE
54
55 auto const mptIssuance = view.read(keylet::mptIssuance(mptShare.getMptID()));
56 if (mptIssuance == nullptr)
57 return false; // zero MPToken won't block deletion of MPTokenIssuance
58
59 auto const issuer = mptIssuance->getAccountID(sfIssuer);
60 auto const mptIssuer = view.read(keylet::account(issuer));
61 if (mptIssuer == nullptr)
62 {
63 // LCOV_EXCL_START
64 UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null MPToken issuer");
65 return false;
66 // LCOV_EXCL_STOP
67 }
68
69 if (!mptIssuer->isFieldPresent(sfVaultID))
70 return false; // not a Vault pseudo-account, common case
71
72 auto const vault = view.read(keylet::vault(mptIssuer->getFieldH256(sfVaultID)));
73 if (vault == nullptr)
74 { // LCOV_EXCL_START
75 UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null vault");
76 return false;
77 // LCOV_EXCL_STOP
78 }
79
80 return isAnyFrozen(view, {issuer, account}, vault->at(sfAsset), depth + 1);
81}
82
83bool
85 ReadView const& view,
86 AccountID const& account,
87 Issue const& asset,
88 Issue const& asset2)
89{
90 return isFrozen(view, account, asset.currency, asset.account) ||
91 isFrozen(view, account, asset2.currency, asset2.account);
92}
93
94bool
96 ReadView const& validLedger,
97 ReadView const& testLedger,
99 char const* reason)
100{
101 bool ret = true;
102
103 if (validLedger.header().seq < testLedger.header().seq)
104 {
105 // valid -> ... -> test
106 auto hash = hashOfSeq(
107 testLedger, validLedger.header().seq, beast::Journal{beast::Journal::getNullSink()});
108 if (hash && (*hash != validLedger.header().hash))
109 {
110 JLOG(s) << reason << " incompatible with valid ledger";
111
112 JLOG(s) << "Hash(VSeq): " << to_string(*hash);
113
114 ret = false;
115 }
116 }
117 else if (validLedger.header().seq > testLedger.header().seq)
118 {
119 // test -> ... -> valid
120 auto hash = hashOfSeq(
121 validLedger, testLedger.header().seq, beast::Journal{beast::Journal::getNullSink()});
122 if (hash && (*hash != testLedger.header().hash))
123 {
124 JLOG(s) << reason << " incompatible preceding ledger";
125
126 JLOG(s) << "Hash(NSeq): " << to_string(*hash);
127
128 ret = false;
129 }
130 }
131 else if (
132 (validLedger.header().seq == testLedger.header().seq) &&
133 (validLedger.header().hash != testLedger.header().hash))
134 {
135 // Same sequence number, different hash
136 JLOG(s) << reason << " incompatible ledger";
137
138 ret = false;
139 }
140
141 if (!ret)
142 {
143 JLOG(s) << "Val: " << validLedger.header().seq << " "
144 << to_string(validLedger.header().hash);
145
146 JLOG(s) << "New: " << testLedger.header().seq << " " << to_string(testLedger.header().hash);
147 }
148
149 return ret;
150}
151
152bool
154 uint256 const& validHash,
155 LedgerIndex validIndex,
156 ReadView const& testLedger,
158 char const* reason)
159{
160 bool ret = true;
161
162 if (testLedger.header().seq > validIndex)
163 {
164 // Ledger we are testing follows last valid ledger
165 auto hash =
166 hashOfSeq(testLedger, validIndex, beast::Journal{beast::Journal::getNullSink()});
167 if (hash && (*hash != validHash))
168 {
169 JLOG(s) << reason << " incompatible following ledger";
170 JLOG(s) << "Hash(VSeq): " << to_string(*hash);
171
172 ret = false;
173 }
174 }
175 else if ((validIndex == testLedger.header().seq) && (testLedger.header().hash != validHash))
176 {
177 JLOG(s) << reason << " incompatible ledger";
178
179 ret = false;
180 }
181
182 if (!ret)
183 {
184 JLOG(s) << "Val: " << validIndex << " " << to_string(validHash);
185
186 JLOG(s) << "New: " << testLedger.header().seq << " " << to_string(testLedger.header().hash);
187 }
188
189 return ret;
190}
191
194{
195 std::set<uint256> amendments;
196
197 if (auto const sle = view.read(keylet::amendments()))
198 {
199 if (sle->isFieldPresent(sfAmendments))
200 {
201 auto const& v = sle->getFieldV256(sfAmendments);
202 amendments.insert(v.begin(), v.end());
203 }
204 }
205
206 return amendments;
207}
208
211{
213
214 if (auto const sle = view.read(keylet::amendments()))
215 {
216 if (sle->isFieldPresent(sfMajorities))
217 {
218 using tp = NetClock::time_point;
219 using d = tp::duration;
220
221 auto const majorities = sle->getFieldArray(sfMajorities);
222
223 for (auto const& m : majorities)
224 ret[m.getFieldH256(sfAmendment)] = tp(d(m.getFieldU32(sfCloseTime)));
225 }
226 }
227
228 return ret;
229}
230
232hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
233{
234 // Easy cases...
235 if (seq > ledger.seq())
236 {
237 JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq() << " future";
238 return std::nullopt;
239 }
240 if (seq == ledger.seq())
241 return ledger.header().hash;
242 if (seq == (ledger.seq() - 1))
243 return ledger.header().parentHash;
244
245 if (int const diff = ledger.seq() - seq; diff <= 256)
246 {
247 // Within 256...
248 auto const hashIndex = ledger.read(keylet::skip());
249 if (hashIndex)
250 {
251 XRPL_ASSERT(
252 hashIndex->getFieldU32(sfLastLedgerSequence) == (ledger.seq() - 1),
253 "xrpl::hashOfSeq : matching ledger sequence");
254 STVector256 vec = hashIndex->getFieldV256(sfHashes);
255 if (vec.size() >= diff)
256 return vec[vec.size() - diff];
257 JLOG(journal.warn()) << "Ledger " << ledger.seq() << " missing hash for " << seq << " ("
258 << vec.size() << "," << diff << ")";
259 }
260 else
261 {
262 JLOG(journal.warn()) << "Ledger " << ledger.seq() << ":" << ledger.header().hash
263 << " missing normal list";
264 }
265 }
266
267 if ((seq & 0xff) != 0)
268 {
269 JLOG(journal.debug()) << "Can't get seq " << seq << " from " << ledger.seq() << " past";
270 return std::nullopt;
271 }
272
273 // in skiplist
274 auto const hashIndex = ledger.read(keylet::skip(seq));
275 if (hashIndex)
276 {
277 auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
278 XRPL_ASSERT(lastSeq >= seq, "xrpl::hashOfSeq : minimum last ledger");
279 XRPL_ASSERT((lastSeq & 0xff) == 0, "xrpl::hashOfSeq : valid last ledger");
280 auto const diff = (lastSeq - seq) >> 8;
281 STVector256 vec = hashIndex->getFieldV256(sfHashes);
282 if (vec.size() > diff)
283 return vec[vec.size() - diff - 1];
284 }
285 JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq() << " error";
286 return std::nullopt;
287}
288
289//------------------------------------------------------------------------------
290//
291// Modifiers
292//
293//------------------------------------------------------------------------------
294
295TER
297 ApplyView& view,
298 AccountID const& owner,
299 std::shared_ptr<SLE>& object,
300 SF_UINT64 const& node)
301{
302 auto const page =
303 view.dirInsert(keylet::ownerDir(owner), object->key(), describeOwnerDir(owner));
304 if (!page)
305 return tecDIR_FULL; // LCOV_EXCL_LINE
306 object->setFieldU64(node, *page);
307 return tesSUCCESS;
308}
309
310/*
311 * Checks if a withdrawal amount into the destination account exceeds
312 * any applicable receiving limit.
313 * Called by VaultWithdraw and LoanBrokerCoverWithdraw.
314 *
315 * IOU : Performs the trustline check against the destination account's
316 * credit limit to ensure the account's trust maximum is not exceeded.
317 *
318 * MPT: The limit check is effectively skipped (returns true). This is
319 * because MPT MaximumAmount relates to token supply, and withdrawal does not
320 * involve minting new tokens that could exceed the global cap.
321 * On withdrawal, tokens are simply transferred from the vault's pseudo-account
322 * to the destination account. Since no new MPT tokens are minted during this
323 * transfer, the withdrawal cannot violate the MPT MaximumAmount/supply cap
324 * even if `from` is the issuer.
325 */
326static TER
328 ReadView const& view,
329 AccountID const& from,
330 AccountID const& to,
331 STAmount const& amount)
332{
333 auto const& issuer = amount.getIssuer();
334 if (from == to || to == issuer || isXRP(issuer))
335 return tesSUCCESS;
336
337 return std::visit(
338 [&]<ValidIssueType TIss>(TIss const& issue) -> TER {
339 if constexpr (std::is_same_v<TIss, Issue>)
340 {
341 auto const& currency = issue.currency;
342 auto const owed = creditBalance(view, to, issuer, currency);
343 if (owed <= beast::zero)
344 {
345 auto const limit = creditLimit(view, to, issuer, currency);
346 if (-owed >= limit || amount > (limit + owed))
347 return tecNO_LINE;
348 }
349 }
350 return tesSUCCESS;
351 },
352 amount.asset().value());
353}
354
355[[nodiscard]] TER
357 ReadView const& view,
358 AccountID const& from,
359 AccountID const& to,
360 SLE::const_ref toSle,
361 STAmount const& amount,
362 bool hasDestinationTag)
363{
364 if (auto const ret = checkDestinationAndTag(toSle, hasDestinationTag))
365 return ret;
366
367 if (from == to)
368 return tesSUCCESS;
369
370 if (toSle->isFlag(lsfDepositAuth))
371 {
372 if (!view.exists(keylet::depositPreauth(to, from)))
373 return tecNO_PERMISSION;
374 }
375
376 return withdrawToDestExceedsLimit(view, from, to, amount);
377}
378
379[[nodiscard]] TER
381 ReadView const& view,
382 AccountID const& from,
383 AccountID const& to,
384 STAmount const& amount,
385 bool hasDestinationTag)
386{
387 auto const toSle = view.read(keylet::account(to));
388
389 return canWithdraw(view, from, to, toSle, amount, hasDestinationTag);
390}
391
392[[nodiscard]] TER
393canWithdraw(ReadView const& view, STTx const& tx)
394{
395 auto const from = tx[sfAccount];
396 auto const to = tx[~sfDestination].value_or(from);
397
398 return canWithdraw(view, from, to, tx[sfAmount], tx.isFieldPresent(sfDestinationTag));
399}
400
401TER
403 ApplyView& view,
404 STTx const& tx,
405 AccountID const& senderAcct,
406 AccountID const& dstAcct,
407 AccountID const& sourceAcct,
408 XRPAmount priorBalance,
409 STAmount const& amount,
411{
412 // Create trust line or MPToken for the receiving account
413 if (dstAcct == senderAcct)
414 {
415 if (auto const ter = addEmptyHolding(view, senderAcct, priorBalance, amount.asset(), j);
416 !isTesSuccess(ter) && ter != tecDUPLICATE)
417 return ter;
418 }
419 else
420 {
421 auto dstSle = view.read(keylet::account(dstAcct));
422 if (auto err = verifyDepositPreauth(tx, view, senderAcct, dstAcct, dstSle, j))
423 return err;
424 }
425
426 // Sanity check
427 if (accountHolds(
428 view,
429 sourceAcct,
430 amount.asset(),
433 j) < amount)
434 {
435 // LCOV_EXCL_START
436 JLOG(j.error()) << "doWithdraw: negative balance of broker cover assets.";
437 return tefINTERNAL;
438 // LCOV_EXCL_STOP
439 }
440
441 // Move the funds directly from the broker's pseudo-account to the
442 // dstAcct
443 return accountSend(view, sourceAcct, dstAcct, amount, j, WaiveTransferFee::Yes);
444}
445
446TER
448 ApplyView& view,
449 Keylet const& ownerDirKeylet,
450 EntryDeleter const& deleter,
452 std::optional<uint16_t> maxNodesToDelete)
453{
454 // Delete all the entries in the account directory.
455 std::shared_ptr<SLE> sleDirNode{};
456 unsigned int uDirEntry{0};
457 uint256 dirEntry{beast::zero};
458 std::uint32_t deleted = 0;
459
460 if (view.exists(ownerDirKeylet) &&
461 dirFirst(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
462 {
463 do
464 {
465 if (maxNodesToDelete && ++deleted > *maxNodesToDelete)
466 return tecINCOMPLETE;
467
468 // Choose the right way to delete each directory node.
469 auto sleItem = view.peek(keylet::child(dirEntry));
470 if (!sleItem)
471 {
472 // Directory node has an invalid index. Bail out.
473 // LCOV_EXCL_START
474 JLOG(j.fatal()) << "DeleteAccount: Directory node in ledger " << view.seq()
475 << " has index to object that is missing: " << to_string(dirEntry);
476 return tefBAD_LEDGER;
477 // LCOV_EXCL_STOP
478 }
479
480 LedgerEntryType const nodeType{
481 safe_cast<LedgerEntryType>(sleItem->getFieldU16(sfLedgerEntryType))};
482
483 // Deleter handles the details of specific account-owned object
484 // deletion
485 auto const [ter, skipEntry] = deleter(nodeType, dirEntry, sleItem);
486 if (!isTesSuccess(ter))
487 return ter;
488
489 // dirFirst() and dirNext() are like iterators with exposed
490 // internal state. We'll take advantage of that exposed state
491 // to solve a common C++ problem: iterator invalidation while
492 // deleting elements from a container.
493 //
494 // We have just deleted one directory entry, which means our
495 // "iterator state" is invalid.
496 //
497 // 1. During the process of getting an entry from the
498 // directory uDirEntry was incremented from 'it' to 'it'+1.
499 //
500 // 2. We then deleted the entry at index 'it', which means the
501 // entry that was at 'it'+1 has now moved to 'it'.
502 //
503 // 3. So we verify that uDirEntry is indeed 'it'+1. Then we jam it
504 // back to 'it' to "un-invalidate" the iterator.
505 XRPL_ASSERT(uDirEntry >= 1, "xrpl::cleanupOnAccountDelete : minimum dir entries");
506 if (uDirEntry == 0)
507 {
508 // LCOV_EXCL_START
509 JLOG(j.error()) << "DeleteAccount iterator re-validation failed.";
510 return tefBAD_LEDGER;
511 // LCOV_EXCL_STOP
512 }
513 if (skipEntry == SkipEntry::No)
514 uDirEntry--;
515
516 } while (dirNext(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
517 }
518
519 return tesSUCCESS;
520}
521
522bool
524{
525 return now.time_since_epoch().count() > mark;
526}
527
528} // namespace xrpl
Provide a light-weight way to check active() before string formatting.
Definition Journal.h:180
A generic endpoint for log messages.
Definition Journal.h:40
Stream fatal() const
Definition Journal.h:325
Stream error() const
Definition Journal.h:319
Stream debug() const
Definition Journal.h:301
static Sink & getNullSink()
Returns a Sink which does nothing.
Stream warn() const
Definition Journal.h:313
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:116
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
Definition ApplyView.h:289
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
AccountID account
Definition Issue.h:16
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:26
std::chrono::time_point< NetClock > time_point
Definition chrono.h:46
std::chrono::duration< rep, period > duration
Definition chrono.h:45
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition ReadView.h:90
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual LedgerHeader const & header() const =0
Returns information about the ledger.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:97
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:120
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:456
std::size_t size() const
T is_same_v
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition Indexes.cpp:177
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition Indexes.cpp:307
Keylet const & amendments() noexcept
The index of the amendment table.
Definition Indexes.cpp:193
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:336
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:474
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition Indexes.cpp:171
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:504
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ fhIGNORE_FREEZE
bool dirFirst(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
bool isXRP(AccountID const &c)
Definition AccountID.h:70
std::uint8_t constexpr maxAssetCheckDepth
Maximum recursion depth for vault shares being put as an asset inside another vault; counted from 0.
Definition Protocol.h:252
std::map< uint256, NetClock::time_point > majorityAmendments_t
Definition View.h:74
std::set< uint256 > getEnabledAmendments(ReadView const &view)
Definition View.cpp:193
bool isVaultPseudoAccountFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptShare, int depth)
Definition View.cpp:43
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE const > const &sleDst, beast::Journal j)
TER doWithdraw(ApplyView &view, STTx const &tx, AccountID const &senderAcct, AccountID const &dstAcct, AccountID const &sourceAcct, XRPAmount priorBalance, STAmount const &amount, beast::Journal j)
Definition View.cpp:402
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:34
TER addEmptyHolding(ApplyView &view, AccountID const &accountID, XRPAmount priorBalance, MPTIssue const &mptIssue, beast::Journal journal)
@ tefBAD_LEDGER
Definition TER.h:150
@ tefINTERNAL
Definition TER.h:153
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
STAmount creditLimit(ReadView const &view, AccountID const &account, AccountID const &issuer, Currency const &currency)
Calculate the maximum amount of IOUs that an account can hold.
bool areCompatible(ReadView const &validLedger, ReadView const &testLedger, beast::Journal::Stream &s, char const *reason)
Return false if the test ledger is provably incompatible with the valid ledger, that is,...
Definition View.cpp:95
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, int depth=0)
std::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition View.cpp:232
TERSubset< CanCvtToTER > TER
Definition TER.h:622
static TER withdrawToDestExceedsLimit(ReadView const &view, AccountID const &from, AccountID const &to, STAmount const &amount)
Definition View.cpp:327
@ ahIGNORE_AUTH
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition View.cpp:210
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:523
STAmount creditBalance(ReadView const &view, AccountID const &account, AccountID const &issuer, Currency const &currency)
Returns the amount of IOUs issued by issuer that are held by an account.
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Returns a function that sets the owner on a directory SLE.
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object, SF_UINT64 const &node=sfOwnerNode)
Definition View.cpp:296
bool isTesSuccess(TER x) noexcept
Definition TER.h:651
LedgerEntryType
Identifiers for on-ledger objects.
@ tecDIR_FULL
Definition TER.h:268
@ tecINCOMPLETE
Definition TER.h:316
@ tecNO_LINE
Definition TER.h:282
@ tecNO_PERMISSION
Definition TER.h:286
@ tecDUPLICATE
Definition TER.h:296
TER canWithdraw(ReadView const &view, AccountID const &from, AccountID const &to, SLE::const_ref toSle, STAmount const &amount, bool hasDestinationTag)
Checks that can withdraw funds from an object to itself or a destination.
Definition View.cpp:356
TER checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag)
Checks the destination and tag.
bool isLPTokenFrozen(ReadView const &view, AccountID const &account, Issue const &asset, Issue const &asset2)
Definition View.cpp:84
@ tesSUCCESS
Definition TER.h:225
bool dirNext(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< std::uint16_t > maxNodesToDelete=std::nullopt)
Cleanup owner directory entries on account delete.
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:19
uint256 key
Definition Keylet.h:20
A field with a type known at compile time.
Definition SField.h:301
T time_since_epoch(T... args)
T visit(T... args)