28 KiB
Ledger
Each ledger is an immutable snapshot: header (seq, hashes, close time) + state SHAMap + transaction SHAMap. LedgerMaster is the central coordinator. The module spans Ledger itself, the view hierarchy (ReadView → ApplyView → OpenView/Sandbox/PaymentSandbox), directory primitives, subscription/order-book fan-out (BookListeners, OrderBookDB), governance state (AmendmentTable), pending-save bookkeeping, and a large family of per-object-type helper free functions.
Key Invariants
- Once
setImmutable()is called, the ledger and its SHAMaps cannot change; only immutable ledgers can be shared across threads. Mutable ledgers must not be shared. - Every server always has an open ledger; the open ledger cannot close until previous consensus completes.
- Ledger header hashes to the ledger's identity hash; includes state root, tx root, parent hash, total coins, close time.
LedgerMastertracks:mPubLedger(last published),mValidLedger(last validated),mLedgerHistory(cache).- Validation requires minimum trusted validations (
minVal); filtered by Negative UNL. - Open ledgers store transactions without metadata; closed ledgers store
addVL(tx)||addVL(meta)and produceTxMetaon apply. - Trust-line
sfBalanceis always stored "low account's perspective"; helpers negate when querying from high side. ThesfLowLimit.account < sfHighLimit.accountordering is the trust-line orientation invariant — every access decides which side is "us" viaAccountIDcomparison. - Directory invariant: page keys are chosen so the low 96 bits of every token in an NFT page are strictly less than the page key's low 96 bits; for owner/order-book directories, page 0 is the anchor and
sfIndexPreviouson root points to the tail. PendingSavesinvariant: exactly one ofsaveLedgerAsync/pendSaveValidatedmay run for a given ledger sequence at a time; second caller observesstarted == trueand bails.ApplyStateTablepointer-identity invariant:erase(sle)andupdate(sle)require the exact sameshared_ptrobtained frompeek()on the same view instance. Crossing views is aLogicError.RawStateTablestate-machine collapse:eraseafterinsertremoves the entry entirely (net zero);insertaftereraseupgrades toreplace; double-erase is aLogicError.
Common Bug Patterns
- Modifying a ledger after
setImmutable()corrupts shared state; always checkmImmutablebefore mutation. - Gap detection: if ledgers 603 and 600 exist but 601-602 are missing,
LedgerMasterrequests 602 first, then backfills 601. InboundLedger::gotData()queues data for processing; callingdone()before all data arrives creates incomplete ledgers.checkAcceptwon't accept a ledger that isn't ahead of the last validated ledger; stale validations are silently ignored.- Calling
ApplyViewImpl::apply()twice or using the view after apply: the only valid operation post-apply is destruction. - Passing an SLE from
peek()on view A toerase()/update()on view B:ApplyStateTableenforces pointer identity andLogicErrors. - Forgetting that
read()is change-aware butslesBegin/Enditerates only the base — pending inserts won't appear in SLE iteration onApplyViewBase. - Comparing iterators across different
ReadViewinstances:XRPL_ASSERTfires in debug; UB in release. - Stale
OpenView::txCountordinal in nested/batch views — must usebatch_view_tconstructor to capturebaseTxCount_. - Calling
removeExpired/deleteSLEin preclaim — preclaim isReadView-only; expiry-driven deletion only happens in doApply. - Forgetting that
directSendNoFeeis not[[nodiscard]](forDirectStep.cppcompatibility) — its return must still be inspected. - Accessing trust-line endpoints by raw low/high without first computing orientation — use the trust-line helpers that take
(account, peer)and resolve which slot to touch. - Constructing a
PaymentSandboxfromApplyView&when nested within another payment: the nested form takesPaymentSandbox const*soDeferredCreditschain to the parent. Wrong constructor = double-spend escape. - Updating an SLE then publishing subscriptions before
havePublishedadvances: re-entrant publishers see partial state. - Submitting to
OrderBookDBwhile holdingmLockfrom a callback —setup_ingests under its own write lock and may re-enter. AcceptedLedgerTxconstructor asserts!ledger->open()— constructing from an open ledger aborts in debug.- Calling
closeChannelbeforesfAmount >= sfBalanceinvariant holds — theXRPL_ASSERTinside will abort; fix the calling transactor, not the helper. ReadViewcopy/move constructors always re-initializesles(*this)andtxs(*this)— subclasses must not rely on memberwise copy of those range objects.forEachItemAfterhint-page optimization: if the hint is stale the code falls back to linear scan but still returnsfalseifafterkey is never found; callers must handle this as an invalid cursor.dirIsEmptyrequires both emptysfIndexesand zerosfIndexNext— an empty root page does not mean an empty directory if subsequent pages exist.
Ledger Entry Types
- Defined in
ledger_entries.macrousingLEDGER_ENTRY(type, code, class, name, fields). - Each entry has an
SOTemplatedefining required/optional fields. - Key computation:
Indexes.cppcomputes unique keys (keylets) for each ledger object type. STLedgerEntrywraps the serialized data with type-safe field access.- Pseudo-account types (AMM, Vault, LoanBroker) are discovered by scanning
ltACCOUNT_ROOTSOTemplate forSField::sMD_PseudoAccount-flagged fields; no manual registration.
View Hierarchy
ReadView (abstract, read-only)
└── DigestAwareReadView (adds per-entry digest for CachedView)
└── Ledger (final; owns stateMap_ + txMap_)
└── OpenView (mutable; ReadView + TxsRawView; delta over base)
└── detail::ApplyViewBase (ApplyView + RawView; buffered via ApplyStateTable)
├── ApplyViewImpl (commit path; produces TxMeta; carries deliver_)
├── Sandbox (discardable; flush via apply(RawView&))
└── PaymentSandbox (overrides credit/balance hooks; DeferredCredits)
ApplyStateTable(per-tx buffer): actionscache/insert/modify/erase; generatesTxMetawithsfPreviousFields/sfFinalFields/sfNewFieldsdriven bySField::sMD_*flags; threadssfPreviousTxnID/sfPreviousTxnLgrSeqon affected account roots and trust-line endpoints. Onapply()the buffer is flushed into the baseRawViewand the table is reset; using the table afterward is UB. A byte-equalsfModifiedNodeis silently omitted from metadata.RawStateTable(used byOpenViewandRawStateTable::applyflush): three actions only; state-machine collapse —insert + erase → removed entirely;insert + replace → insert with new SLE;erase + insert (modify) → replace. NoTxMetais produced becauseRawViewis the post-apply mutation surface.- Both tables use
boost::container::pmr::monotonic_buffer_resourcewith a 256 KB initial arena;unique_ptrfor stable address so map allocators work after move. Memory is released only when the table is destroyed — long-lived tables leak peak working set. ReadViewFwdRangeis the iterator/range adapter used forsles/txstraversal; iterator equality is anchored to the owning view (XRPL_ASSERTcross-view comparisons in debug). Virtual clone viaiter_base::copy()enables value-semantics copying. Deferred dereference caches the result inmutable std::optional<value_type> cache_;operator++clears the cache.CachedView(CachedLedger = CachedView<Ledger>): two-level cache — per-viewmap_<key, digest>plus process-wideCachedSLEs(TaggedCache<uint256, SLE const>) keyed by digest. The per-view map useshardened_hashto defeat hash-flooding from adversarial keylet sequences. Hit/hitExpired/miss counters distinguish full hit, digest-known-but-SLE-evicted, and cold miss. The expensivebase_.digest()andbase_.read()calls happen outside the lock;mutex_protects only the key→digest map.- Hooks pattern:
balanceHookIOU/MPT,ownerCountHook(read side, onReadView) andcreditHookIOU/MPT,adjustOwnerCountHook,issuerSelfDebitHookMPT(write side, onApplyView) are no-ops by default;PaymentSandboxoverrides them to prevent within-payment double-spend. isDryRunpath inapply(OpenView&...): fullTxMetais built and returned asstd::optional<TxMeta>, but state changes andrawTxInsertare suppressed. Used for fee simulation.
Directory Structures
Three distinct paged-list flavors, all ltDIR_NODE-based:
- Owner / book directories (
ApplyView::dirInsert/dirAppend/dirRemove/dirDelete): root at page 0;sfIndexNext/sfIndexPreviouslinked; root'ssfIndexPreviouspoints to tail for O(1) append.dirAppendpreserves insertion order (offers only, asserted);dirInsertkeeps sorted order within each page. Page overflow detected via deliberateuint64_twraparound (compile-timestatic_asserted).describecallback brands each new page with type-specific fields (e.g.,sfOwner). - NFToken pages (
NFTokenHelpers): tokens packed intoSTArray-bearing pages, sorted bycompareTokens()(low 96 bits, then full ID). Last page anchored atkeylet::nftpage_max(owner). Split algorithm respects equivalence groups (identical low 96 bits); merge across adjacent pages on remove.fixNFTokenPageLinksamendment changes empty-last-page handling. - Quality-keyed order books (
BookDirs): two-level —succ()finds next quality directory in[root_, getQualityNext(root_)), thencdirFirst/cdirNextwalks pages within that quality.BookDirsiterator transparently crosses quality boundaries.
The Dir class is a simple range adaptor (NFTokenOffer directories + unit tests); next_page() is public to allow page-skipping traversal (used by notTooManyOffers).
Helper Module (include/xrpl/ledger/helpers/)
Free functions per ledger-object type. The asset-agnostic dispatcher is TokenHelpers.h, which routes Asset (std::variant<Issue, MPTIssue>) via std::visit to RippleStateHelpers (IOU) or MPTokenHelpers (MPT).
Conventional split:
- Stateless / preflight-safe checks: take
ReadView const& - State-mutating: take
ApplyView& - Two-phase pattern: read-only preclaim function (
credentials::valid/validDomain) paired with mutating doApply counterpart (verifyDepositPreauth/verifyValidDomain) that prunes expired entries
Key files: AMMHelpers, AccountRootHelpers, CredentialHelpers, DelegateHelpers, DirectoryHelpers, EscrowHelpers, MPTokenHelpers, NFTokenHelpers, OfferHelpers, PaymentChannelHelpers, PermissionedDEXHelpers, RippleStateHelpers, TokenHelpers, VaultHelpers.
Policy enums (used to avoid bare bools): FreezeHandling, AuthHandling, SpendableHandling, WaiveTransferFee, AllowMPTOverflow, AuthType (StrongAuth/WeakAuth/Legacy — Legacy maps to StrongAuth for MPT, WeakAuth for IOU), TruncateShares.
AMM Rounding Contract
The pool invariant sqrt(asset1 × asset2) >= LPTokenBalance is non-negotiable, so every formula has explicit directional rounding:
- Swap-in: output rounds down (trader gets less, pool retains).
- Swap-out: input rounds up (trader pays more).
- LP token deposit: tokens down, assets up.
- LP token withdrawal: tokens up, assets down.
fixAMMv1_1 introduced per-step rounding (vs end-only); fixAMMv1_3 extended this discipline to LP/deposit/withdraw paths via multiply(balance, frac, mode) and the getRoundedAsset/getRoundedLPTokens wrappers (two overloads each — direct and lambda-deferred). Pre-amendment paths must be preserved for historic replay.
adjustLPTokens: avoids precision loss when adding small token amounts to large LPTokensBalance by computing (balance + tokens) - balance rather than tokens. Becomes a no-op under fixAMMv1_3.
changeSpotPriceQuality: aligns AMM synthetic offer to CLOB best quality. Solves a quadratic (or linear, for the alternate constraint) and takes the smaller binding result. fixAMMv1_1 switched the starting side to always-XRP-first to avoid XRP-drop discretization undershoot. detail::reduceOffer applies 0.01% rescue multiplier when quality still falls below target.
solveQuadraticEqSmallest() uses the numerically stable "citardauq" formula (Blinn's paper) to avoid catastrophic cancellation when b > 0 in the standard form.
MPT Specifics
OutstandingAmountcan transiently exceedMaximumAmountduring payment-engine routing —AllowMPTOverflow::Yesraises the ceiling toUINT64_MAXfor that case; direct sends useNoand strict cap. ThefixSecurity3_1_3amendment makesaccountSendMultiaccumulate in exactuint64_t(notSTAmount/Number) to avoid 19-digit precision loss in aggregate overflow checks.selfDebitfield onIssuerValueMPTinPaymentSandbox::DeferredCreditstracks issuer-as-seller offers because the payment engine credits first;balanceHookSelfIssueMPTcaps available issuance atorigBalance - selfDebit.- Two-phase auth:
requireAuth(preclaim,ReadView) checks the static authorization predicates;enforceMPTokenAuthorization(doApply,ApplyView) handles the case where a domain-authorized holder lacks anMPTokenSLE in preclaim — it lazily allocates the SLE on the fly and consumes thepriorBalancereserve. lockEscrowMPTdoes NOT changeOutstandingAmount(tokens are still in circulation while escrowed);unlockEscrowMPTdecreasesOutstandingAmountonly by the fee delta (gross - net) underfixTokenEscrowV1.isVaultPseudoAccountFrozen()recursively checks whether a vault-backed MPT issuance is frozen by the vault's underlying asset; adepthparameter bounds recursion (purely defensive — nested vaults cannot currently be created).
IOU Trust-Line Specifics
- Orientation: every trust line stores its endpoints in fixed
sfLowLimit/sfHighLimitslots based onAccountIDcomparison. Helpers likeaccountHolds,accountFunds,trustCreate,trustDeletetake(account, peer)and resolve the slot internally — never index by raw low/high outside the helpers. - Three freeze tiers:
isIndividualFrozen(issuer froze this specific holder),isFrozen(global freeze flag),isDeepFrozen(transitive freeze through deep-freeze chains). Each has distinct policy implications for sends, offers, and AMM participation; helper queries take aFreezeHandlingenum to select policy. rippleCredit/rippleSendapply transfer fees only when the issuer is neither endpoint andWaiveTransferFee::No; reserve/quality limits enforced viaaccountFundsrather than raw balance.- Trust-line deletion (
trustDelete) requires both endpoints to be at default state (zero balance, default limits/flags); helpers compute "default" against the post-tx state, not raw fields. updateTrustLine(static helper inRippleStateHelpers.cpp): when a sender's balance crosses zero and meets seven specific conditions (zero limit, zero quality flags, no freeze, etc.), it releases the sender's reserve and signals the caller to delete the line.
Credential System
- Two-phase enforcement mirrors the
ReadView/ApplyViewsplit:credentials::valid()andcredentials::validDomain()are read-only (preclaim);verifyDepositPreauth()andverifyValidDomain()are mutating (doApply) and delete expired credential objects as a side effect. credentials::deleteSLE()handles two owner directories (issuer and subject) with reserve accounting that shifts ownership atlsfAcceptedtime. Before acceptance only the issuer pays the reserve; after, the subject does.checkFields()validatessfCredentialIDs(STVector256);checkArray()validatesSTArraycredential pairs and usessha512Half(issuer, credentialType)for duplicate detection.authorizedDepositPreauth()uses alifeExtendervector to keepSLE constshared pointers alive while theSlice-based set is in scope — forgetting this causes dangling pointer reads.
Pseudo-Accounts
Synthetic AccountRoot SLEs owned by protocol objects (AMM, Vault, LoanBroker). Address derived from sha512Half(attempt, parentHash, ownerKey) → RIPESHA in a loop up to maxAccountAttempts = 256 (consensus-critical constant). Flags lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth; sfSequence = 0 under featureSingleAssetVault/featureLendingProtocol. isPseudoAccount(sle, filter?) checks for any field tagged SField::sMD_PseudoAccount (currently sfAMMID, sfVaultID, sfLoanBrokerID). Pseudo-accounts bypass reserve requirements in xrpLiquid.
Amendment checks are the caller's responsibility, not createPseudoAccount's. The function asserts the passed ownerField carries sMD_PseudoAccount.
Vault Math
VaultHelpers converts between asset and share denominations using sfScale (decimal precision) and tracks sfLossUnrealized separately on deposit vs withdraw paths — the asymmetry is intentional: deposits price against gross sfAssetsTotal; withdrawals subtract sfLossUnrealized first (existing holders bear the loss, not new depositors). TruncateShares policy controls whether sub-unit share remainders are rounded to zero or rejected.
Empty-vault bootstrap: when sfAssetsTotal == 0, initial share allocation is assets × 10^sfScale (truncated), establishing the starting exchange rate. This reduces susceptibility to the first-depositor donation attack.
Ledger Timing (LedgerTiming.h)
Adaptive close-time binning prevents clock-skew disagreements. Resolutions: {10, 20, 30, 60, 90, 120} seconds; default 30, genesis 10. Adjustment is asymmetric:
- On disagreement, coarsen every ledger (
decreaseLedgerTimeResolutionEvery = 1) - On agreement, refine only every 8th ledger (
increaseLedgerTimeResolutionEvery = 8)
roundCloseTime is epoch-anchored (uses time_since_epoch(), not local offset) for deterministic agreement. effCloseTime enforces strict monotonicity: max(rounded, priorCloseTime + 1s). time_point{} is the sentinel for "no agreed close time" and is returned unchanged.
Skip List
Two-tier on-ledger structure for historical hash lookup:
keylet::skip()(the rolling 256-page): hashes of the 256 immediate ancestors; updated every ledger.keylet::skip(seq): every 256-aligned ledger stores a permanent record.getCandidateLedger(seq)rounds up to the nearest 256-aligned sequence.
Non-aligned ledgers older than 256 are unreachable — hashOfSeq returns nullopt.
Canonical Transaction Ordering (CanonicalTXSet)
Retry queue between consensus passes. Sort key: (account ⊕ salt, SeqProxy, txId).
- Salt:
LedgerHashXORed intoAccountID; prevents account-address mining for persistent ordering advantage. Refreshed byreset()each round. SeqProxy: sequences sort before tickets unconditionally (soTicketCreatealways applies before ticket consumers).popAcctTransaction(): returns next eligible same-account tx — either ticket-based, or sequence exactly+1from current.
Key::operator== compares only txId_ (asymmetric with operator<).
Subscription Fan-Out
BookListeners and OrderBookDB together drive WebSocket book-stream subscriptions:
BookListeners: per-book (Book-keyed) registry ofInfoSub::wptr.publishbuilds aMultiApiJsononce and dispatches by API version, expiring dead weak pointers in-place. Locking is per-listener-set (std::recursive_mutex), not global.OrderBookDB: scans the ledger'sdirNodeindex to buildxrpBooks_(XRP-side, O(1) checked) andallBooks_(IOU/MPT, scanned). Scans are throttled bysetup_seq_— only re-scan on ledger change.publishOrderBookwalks affected listeners;havePublisheddedups so the same ledger isn't re-fanned-out across overlapping subscription updates.- Ingest path under
mLock(write); subscribe/unsubscribe under read lock. Callbacks must not re-entersetup_. havePublishedis ahash_set<uint64_t>shared across allBookListeners::publish()calls for a single transaction, preventing duplicate delivery to subscribers registered on multiple affected books.
Accepted Ledger Tx (AcceptedLedgerTx)
Eagerly-materialized projection of a transaction-in-ledger: tx, meta, deserialized account fields, affected-accounts list, JSON-serialization cache. Construction is the heavyweight step; downstream consumers (RPC, subscription) read fields by reference. The class is immutable post-construction; thread-safe to share.
Constructor asserts !ledger->open(). For ttOFFER_CREATE transactions where account != amount.getIssuer(), owner_funds is injected into mJson via accountFunds(fhIGNORE_FREEZE, ahIGNORE_AUTH) — a read-time annotation for order-book subscribers, not ledger state.
getEscMeta() returns mRawMeta as a SQL blob literal; used by Node.cpp for transaction DB inserts.
Pending Saves (PendingSaves)
State machine tracking in-flight Ledger → DB writes. Each entry keyed by ledger sequence; transitions requested → started → finished. pendSave is idempotent — second caller for the same sequence is a no-op. Synchronous callers in shouldWork(seq, true) block on a condition_variable until the in-progress write completes. getSnapshot() returns a copy of the map; LedgerMaster::getValidatedRange() uses it to exclude in-progress sequences from the reported range.
Amendment Table (AmendmentTable)
Governance state for protocol amendments. Each amendment has a VoteBehavior:
DefaultYes— vote yes unless config overridesDefaultNo— vote no unless config overridesObsolete— never vote yes, even with config override;LogicErrorif config tries
TrustedVotes tracks per-validator amendment votes across ledgers with anti-flapping (a validator must consistently vote for N consecutive flag ledgers before counting toward majority). The flag-ledger cadence and majority threshold are consensus-critical constants. doVoting produces the EnableAmendment/Veto pseudo-transactions inserted at flag ledger close.
Two-layer API: the pure-virtual doVoting(LedgerIndex, set<uint256>, majorityAmendments_t) is wrapped by a non-virtual doVoting(shared_ptr<ReadView const>, ...) that calls getEnabledAmendments() and getMajorityAmendments() from View.h. This decouples the implementation from ledger view types.
needValidatedLedger(seq) is an optimization gate — most ledgers have no effect on amendment voting; only flag ledgers need the full doValidatedLedger pass.
Review Checklist
- New ledger entry types: add to
ledger_entries.macro, implement keylet inIndexes.cpp, verify acquisition code inInboundLedger/LedgerMaster, checkLedgerCleanerhandling. - New pseudo-account types: tag the key field with
SField::sMD_PseudoAccountinsfields.macro;isPseudoAccountpicks it up automatically. Caller, notcreatePseudoAccount, owns the amendment gate. - New asset operations: extend
Assetvariant + add branches inTokenHelpers.hdispatchers. Don't reach into IOU- or MPT-specific helpers directly unless intentionally bypassing the dispatch layer. - Helper functions touching balances: respect the read/write hook protocol so
PaymentSandboxcorrectly defers credits. - AMM math changes: gate behind an amendment; preserve the pre-amendment formula path.
- Directory changes: account for both legacy (unsorted) and modern pages in iteration; verify
cdirNext/dirNextcursor semantics if deleting during iteration (seecleanupOnAccountDeleteworkaround inView.cpparound line 485). - New amendment: register
VoteBehavior; ifObsolete, ensure no config path can flip it; add toTrustedVotesaccounting if it should be majority-tracked. - New subscription stream: if order-book-related, register with
BookListeners; for ledger-wide streams useOrderBookDB::havePublishedto dedup. - Escrow with non-XRP assets: use
escrowUnlockApplyHelper<T>(template specialized forIssue/MPTIssue); checkcreateAssetflag gating andlockedRatecapping logic. - Delegate transactions: call
checkTxPermissionfirst; only callloadGranularPermissionwhen broad permission check fails.
Key Patterns
Immutability Guard
// After this, no mutations allowed on the ledger or its SHAMaps
inline void SHAMap::setImmutable()
{
XRPL_ASSERT(state_ != SHAMapState::Invalid, "...");
state_ = SHAMapState::Immutable;
}
// VERIFY: code never calls peek()/insert()/erase() after setImmutable()
New Ledger Entry Keylet
// REQUIRED: every new ledger entry type needs unique keylet computation
Keylet keylet::myEntry(AccountID const& id)
{
return {ltMY_ENTRY,
sha512Half(std::uint16_t(spaceMyEntry), id)};
}
// Also add to ledger_entries.macro and Indexes.cpp
Peek/Update Contract
// peek() returns shared_ptr<SLE>; you MUST call update() or erase() with
// the SAME pointer on the SAME view. Crossing views is a LogicError.
auto sle = view.peek(keylet::account(id));
sle->setFieldU32(sfSequence, seq + 1);
view.update(sle); // promotes cache → modify in ApplyStateTable
Two-Phase Expiry Cleanup
// preclaim (ReadView) — detect but don't mutate
if (credentials::validDomain(view, domainID, account) == tecEXPIRED)
/* allow through; doApply will clean up */;
// doApply (ApplyView) — mutate
if (auto ter = verifyValidDomain(view, domainID, account, j); ter != tesSUCCESS)
return ter; // expired credentials deleted as side effect
Directional Multiply (AMM)
// post-fixAMMv1_3: rounding mode at the final multiply, not at toSTAmount
auto const tokens = getRoundedLPTokens(
rules,
lptAMMBalance,
[&] { return /* fractional formula */; },
IsDeposit::Yes); // → Number::downward
Nested PaymentSandbox
// Inside a payment step that needs its own sandbox: chain DeferredCredits
PaymentSandbox inner(&outer); // PaymentSandbox const* form
// ... inner.creditHookIOU(...) defers against the outer's table too
inner.apply(outer); // flushes deferred credits up one level
Subscription Fan-Out
// publish once, dispatch by version; havePublished shared across all books
MultiApiJson msg = buildBookUpdate(...);
listeners.publish(msg, havePublished);
db.havePublished(ledgerSeq); // dedup across overlapping streams
Sandbox Commit-or-Discard
Sandbox sb(&ctx_.view());
// ... all mutations through sb ...
if (result == tesSUCCESS)
sb.apply(ctx_.rawView());
// else sb destroyed → changes evaporate
Key Files
src/xrpld/app/ledger/Ledger.h/src/libxrpl/ledger/Ledger.cpp— ledger class, genesis/successor/load constructors, immutable transition, skip list, NegUNLsrc/xrpld/app/ledger/detail/LedgerMaster.cpp— central coordinatorsrc/xrpld/app/ledger/detail/InboundLedger.cpp— ledger acquisitioninclude/xrpl/protocol/detail/ledger_entries.macro— entry type definitionssrc/libxrpl/protocol/Indexes.cpp— keylet computationinclude/xrpl/ledger/ReadView.h+ApplyView.h+OpenView.h+RawView.h— view interface hierarchyinclude/xrpl/ledger/detail/ApplyStateTable.h/RawStateTable.h/ApplyViewBase.h/ReadViewFwdRange.h+.ipp— buffered mutation tables, range adapters,TxMetagenerationinclude/xrpl/ledger/Sandbox.h/PaymentSandbox.h/ApplyViewImpl.h— concrete view typesinclude/xrpl/ledger/CachedView.h/CachedSLEs.h— two-level SLE cache (hardened-hash inner map)include/xrpl/ledger/CanonicalTXSet.h— retry-pass deterministic orderinginclude/xrpl/ledger/LedgerTiming.h— close-time binninginclude/xrpl/ledger/Dir.h/BookDirs.h— directory iterationinclude/xrpl/ledger/BookListeners.h/OrderBookDB.h— subscription fan-outinclude/xrpl/ledger/AcceptedLedgerTx.h— eager tx-in-ledger projectioninclude/xrpl/ledger/AmendmentTable.h— governance state,VoteBehavior,TrustedVotesinclude/xrpl/ledger/PendingSaves.h— in-flight DB write coalescinginclude/xrpl/ledger/View.h— free-function utility layer (expiry,hashOfSeq,areCompatible,dirLink,canWithdraw,cleanupOnAccountDelete)include/xrpl/ledger/helpers/*— per-object-type free functionssrc/libxrpl/ledger/helpers/*— implementations (AMM math, NFT page split, credential lifecycle, MPT overflow, vault math)src/libxrpl/ledger/ApplyView.cpp— directorycreateRoot,findPreviousPage,insertKey,insertPage(innamespace xrpl::directory)src/libxrpl/ledger/PaymentSandbox.cpp—DeferredCreditsIOU/MPT shadow tables, post-switchover balance algorithmsrc/libxrpl/ledger/OpenView.cpp—RawStateTable+txs_mapdelta accumulation, PMR arena