> subsystem-summary-of-ledger
read this skill for a token-efficient summary of the ledger subsystem
curl "https://skillshub.wtf/stellar/stellar-core/subsystem-summary-of-ledger?format=md"Ledger Subsystem — Technical Summary
Overview
The ledger subsystem is the core of stellar-core's state management. It orchestrates ledger closing (applying transaction sets to produce new ledger states), manages the "last closed ledger" (LCL) state, provides a transactional in-memory layer (LedgerTxn) for reading/writing ledger entries during transaction processing, maintains in-memory Soroban contract state, manages Soroban network configuration, and produces ledger close metadata for downstream consumers (e.g., Horizon).
The subsystem has a two-thread architecture: a main thread handles consensus and publishes state, while a apply thread executes transactions and commits state. The invariant LCL <= A <= Q <= H tracks ledger progress across these threads (H=heard, Q=queued, A=applying, LCL=last closed).
Key Files
- LedgerManager.h / LedgerManagerImpl.h / LedgerManagerImpl.cpp — Abstract interface and implementation for ledger lifecycle: applying, closing, state management, parallel apply orchestration.
- LedgerTxn.h / LedgerTxnImpl.h / LedgerTxn.cpp — Nestable in-memory transactional layer over the database for ledger entries; core data mutation path.
- LedgerTxnEntry.h / .cpp — Handle types (
LedgerTxnEntry,ConstLedgerTxnEntry) for accessing active entries in aLedgerTxn. - LedgerTxnHeader.h / .cpp — Handle type for accessing the
LedgerHeaderwithin aLedgerTxn. - LedgerTxnOfferSQL.cpp — SQL-specific bulk operations for offer entries (upsert/delete) used by
LedgerTxnRoot. - InternalLedgerEntry.h / .cpp — Extended ledger entry types (
InternalLedgerEntry,InternalLedgerKey) that wrap XDRLedgerEntry/LedgerKeywith additional internal-only types (sponsorship tracking, max-seq-num-to-apply). - NetworkConfig.h / .cpp —
SorobanNetworkConfig: reads/writes all Soroban-related configuration from ledger entries, provides fee and resource limit accessors. - InMemorySorobanState.h / .cpp —
InMemorySorobanState: in-memory cache of all live Soroban contract data, code, and TTL entries for fast lookups during transaction execution. - LedgerStateSnapshot.h / .cpp — Unified read-only snapshot interfaces (
LedgerSnapshot,CompleteConstLedgerState) abstracting over SQL and BucketList backends. - LedgerCloseMetaFrame.h / .cpp — Wrapper around
LedgerCloseMetaXDR for building per-ledger metadata during close. - LedgerEntryScope.h / .cpp — Compile-time and runtime scope checking for
LedgerEntryusage across threads and ledger phases (ScopedLedgerEntry<S>,LedgerEntryScope<S>). - SharedModuleCacheCompiler.h / .cpp — Multi-threaded Wasm contract compilation for Soroban module cache.
- SorobanMetrics.h / .cpp — Aggregated metrics for Soroban transaction execution (CPU, memory, IO, fees).
- LedgerHeaderUtils.h / .cpp — Utilities for storing/loading
LedgerHeaderto/from the database. - LedgerTypeUtils.h / .cpp — Helpers for TTL key derivation, liveness checks, and Soroban entry classification.
- LedgerHashUtils.h — Hash functions for
LedgerKey,Asset,InternalLedgerKeyused in unordered containers. - TrustLineWrapper.h / .cpp — Safe wrapper for trust line operations (balance, liabilities, authorization) with special issuer handling.
- CheckpointRange.h / .cpp — Represents half-open ranges of history checkpoints.
- LedgerRange.h / .cpp — Represents half-open ranges of ledger sequence numbers.
- FlushAndRotateMetaDebugWork.h / .cpp — Background work item for flushing and rotating debug meta streams.
- P23HotArchiveBug.h / .cpp — Protocol 23 hot archive corruption verification and fix data.
Key Classes and Data Structures
LedgerManager (abstract interface)
Defines the public API for ledger lifecycle management. States: LM_BOOTING_STATE, LM_SYNCED_STATE, LM_CATCHING_UP_STATE.
Key methods:
valueExternalized(LedgerCloseData)— Called by Herder when SCP reaches consensus; triggers ledger apply.applyLedger(LedgerCloseData, calledViaExternalize)— Core ledger application: processes fees/seqnums, applies transactions, applies upgrades, seals to buckets/DB.advanceLedgerStateAndPublish(...)— Post-apply: publishes to history, updates LCL, triggers next ledger.getLastClosedLedgerHeader()/getLastClosedLedgerNum()— Access LCL state.getLastClosedSnapshot()— Returns immutable BucketList snapshot of LCL.getLastClosedSorobanNetworkConfig()— Returns Soroban config as of LCL (main thread only).startNewLedger()/loadLastKnownLedger()— Startup paths.setLastClosedLedger(...)— Used by catchup after bucket-apply to reset LCL.getSorobanMetrics()/getModuleCache()— Access apply-thread state.markApplyStateReset()— Signals that apply state must be re-initialized (e.g., after catchup).
Genesis constants: GENESIS_LEDGER_SEQ=1, GENESIS_LEDGER_VERSION=0, GENESIS_LEDGER_BASE_FEE=100, GENESIS_LEDGER_BASE_RESERVE=100000000, GENESIS_LEDGER_TOTAL_COINS=1000000000000000000.
LedgerManagerImpl
Concrete implementation. Owns ApplyState and cached LCL state.
Key members:
mApplyState(ApplyState) — Encapsulates all state for the apply thread (metrics, module cache, in-memory Soroban state). Has phase machine:SETTING_UP_STATE → READY_TO_APPLY → APPLYING → COMMITTING → READY_TO_APPLY.mLastClosedLedgerState(CompleteConstLedgerStatePtr) — Complete snapshot of LCL (bucket snapshot, hot archive snapshot, soroban config, header, HAS).mLastClose(VirtualClock::time_point) — Timestamp of last close.mLedgerStateMutex— Guards ledger state during apply.mCurrentlyApplyingLedger— Indicates background apply thread is active.mNextMetaToEmit— Buffered meta frame awaiting emission.mMetaStream/mMetaDebugStream— XDR output streams for ledger close meta.mState— CurrentLedgerManager::State.
Key internal methods:
processFeesSeqNums(...)— Charges fees and increments sequence numbers before tx apply.applyTransactions(...)— Dispatches toapplySequentialPhase(classic) andapplyParallelPhase(Soroban).applyParallelPhase(...)— Orchestrates parallel Soroban execution via stages/clusters.applySorobanStages(...)/applySorobanStageClustersInParallel(...)— Spawns worker threads for Soroban tx execution.applyThread(...)— Entry point for each Soroban worker thread.sealLedgerTxnAndStoreInBucketsAndDB(...)— Seals the LedgerTxn, writes to BucketList and DB, runs invariants.storePersistentStateAndLedgerHeaderInDB(...)— Persists ledger header and HAS.advanceBucketListSnapshotAndMakeLedgerState(...)— Updates BucketList snapshot, constructsCompleteConstLedgerState.advanceLastClosedLedgerState(...)— Updates cached LCL variables.prefetchTransactionData(...)/prefetchTxSourceIds(...)— Pre-loads entries into cache before apply.ledgerCloseComplete(...)— Called afteradvanceLedgerStateAndPublishcompletes all post-close work.
ApplyState (inner class of LedgerManagerImpl)
Encapsulates state used by the primary apply thread. Only the primary apply thread may mutate it. Soroban execution threads see it as immutable.
Key members:
mMetrics(LedgerApplyMetrics) — All apply-related metrics.mModuleCache(rust::Box<SorobanModuleCache>) — Reusable compiled Soroban module cache.mCompiler(SharedModuleCacheCompiler) — Background contract compiler, non-null only during compilation.mInMemorySorobanState(InMemorySorobanState) — Live Soroban state cache.mPhase— Current phase enum.
Phase transitions: SETTING_UP_STATE → READY_TO_APPLY ↔ APPLYING → COMMITTING → READY_TO_APPLY. Also READY_TO_APPLY → SETTING_UP_STATE when catchup resets state.
AbstractLedgerTxnParent (abstract)
Base class for anything that can be the parent of a LedgerTxn. Provides interface for committing children, querying offers, loading entries, prefetching.
AbstractLedgerTxn (extends AbstractLedgerTxnParent)
Adds transaction semantics: commit(), rollback(), loadHeader(), create(), load(), erase(), loadWithoutRecord(), restoreFromLiveBucketList(), markRestoredFromHotArchive(), bulk-load methods (createWithoutLoading, updateWithoutLoading, eraseWithoutLoading), and sealed-state extraction (getChanges(), getDelta(), getAllEntries()).
LedgerTxn (concrete, extends AbstractLedgerTxn)
Nestable in-memory transaction. Can be a child of either another LedgerTxn or LedgerTxnRoot. Thread-affine: must be used from the same thread throughout its lifetime.
Key inner class LedgerTxn::Impl:
mEntry(EntryMap = UnorderedMap<InternalLedgerKey, LedgerEntryPtr>) — Map of all entries modified/created/deleted in this transaction.mActive(UnorderedMap<InternalLedgerKey, shared_ptr<EntryImplBase>>) — Tracks which entries are currently "active" (safe to access). Entries are deactivated when a child is opened.mMultiOrderBook— In-memory order book for offers, grouped by asset pair, sorted by best-offer relation. Only consulted whenmActiveis empty.mWorstBestOffer(WorstBestOfferMap) — Cache to accelerate repeatedloadBestOffercalls in nested LedgerTxns (critical for offer crossing loops).mHeader— Copy ofLedgerHeaderfor this transaction level.mRestoredEntries(RestoredEntries) — Tracks entries restored from hot archive or live bucket list.mIsSealed— Once sealed, no more mutations; ready for extraction.mConsistency—EXACTorEXTRA_DELETES(relaxed foreraseWithoutLoading).
Commit flow: On commit(), all entries in mEntry are merged into the parent. If the parent is another LedgerTxn, entries go into the parent's mEntry map. If the parent is LedgerTxnRoot, entries are written to the database via SQL.
LedgerTxnRoot (concrete, extends AbstractLedgerTxnParent)
The root of the LedgerTxn hierarchy. Connects to the database. One per application.
Key inner class LedgerTxnRoot::Impl:
mEntryCache(RandomEvictionCache<LedgerKey, CacheEntry>) — LRU cache of recently-loaded entries from DB.mBestOffers— Cache of best offers per asset pair, loaded lazily from DB in batches.mSearchableBucketListSnapshot— BucketList snapshot for Soroban entry lookups.mInMemorySorobanState— Reference to the in-memory Soroban state for fast lookups of contract data/code/TTL.mSession— Database session wrapper.mBulkLoadBatchSize— Controls batch size for SQL bulk loads.mTransaction— Active SQL transaction (SERIALIZABLE isolation).- Prefetch tracking:
mPrefetchHits,mPrefetchMisses.
LedgerEntryPtr
A smart pointer wrapper around InternalLedgerEntry with state tracking: INIT (created at this level), LIVE (modified at this level), DELETED (deleted at this level). Used within LedgerTxn::Impl::mEntry.
LedgerTxnEntry / ConstLedgerTxnEntry
Client-facing handles for accessing ledger entries. Uses double-indirection (weak_ptr to Impl) to enforce the invariant that only the innermost LedgerTxn's entries are accessible. Move-only (no copy). Key methods: current(), currentGeneralized(), deactivate(), erase().
LedgerTxnHeader
Handle for accessing the LedgerHeader within a LedgerTxn. Same double-indirection pattern as LedgerTxnEntry.
InternalLedgerEntry / InternalLedgerKey
Extended entry/key types wrapping XDR LedgerEntry/LedgerKey with additional discriminated-union variants:
LEDGER_ENTRY— Normal XDR ledger entry.SPONSORSHIP— Tracks sponsoredID↔sponsoringID relationships.SPONSORSHIP_COUNTER— Counts how many entries an account sponsors.MAX_SEQ_NUM_TO_APPLY— Tracks maximum sequence number constraints.
These are used internally by the LedgerTxn subsystem and are not persisted to the database directly as their own table types—they are tracked alongside the regular ledger entries in the in-memory transaction maps.
SorobanNetworkConfig
Holds all Soroban contract-related network configuration parameters loaded from ledger entries (config settings). Constructed via loadFromLedger().
Key setting groups:
- Contract size:
maxContractSizeBytes,maxContractDataKeySizeBytes,maxContractDataEntrySizeBytes. - Compute:
ledgerMaxInstructions,txMaxInstructions,txMemoryLimit,feeRatePerInstructionsIncrement. - Ledger access:
ledgerMaxDiskReadEntries/Bytes,ledgerMaxWriteLedgerEntries/Bytes,txMaxDiskReadEntries/Bytes,txMaxWriteLedgerEntries/Bytes, per-entry and per-KB fees. - Bandwidth:
ledgerMaxTransactionSizesBytes,txMaxSizeBytes,feeTransactionSize1KB. - Events:
txMaxContractEventsSizeBytes,feeContractEventsSize1KB. - State archival:
StateArchivalSettings,EvictionIterator, rent rates, lifetime bounds. - Parallel execution:
ledgerMaxDependentTxClusters. - SCP timing:
ledgerTargetCloseTimeMilliseconds, nomination/ballot timeout settings. - Cost model:
cpuCostParams,memCostParams(contract cost parameters for the Soroban host).
Static methods like createLedgerEntriesForV20(), createCostTypesForV21/V22/V25(), createAndUpdateLedgerEntriesForV23() handle protocol upgrade initialization of config entries.
InMemorySorobanState
In-memory cache of all live Soroban contract data, contract code, and their TTLs. Updated each ledger. NOT thread-safe for concurrent reads+writes; callers must ensure exclusivity.
Key members:
mContractDataEntries(unordered_set<InternalContractDataMapEntry>) — ContractData entries indexed by TTL key hash, using a custom polymorphic entry type to save memory (avoids storing the key twice).mContractCodeEntries(unordered_map<uint256, ContractCodeMapEntryT>) — ContractCode entries indexed by TTL key hash.mPendingTTLs— Temporary buffer for TTLs arriving before their data during initialization.mContractCodeStateSize,mContractDataStateSize— Running size totals for rent fee computation.
Key methods:
initializeStateFromSnapshot(snap, ledgerVersion)— Populates from BucketList snapshot.updateState(initEntries, liveEntries, deadEntries, header, sorobanConfig)— Incremental per-ledger update.get(LedgerKey)— Returns entry or nullptr.getSize()— Total in-memory state size for rent calculations.recomputeContractCodeSize(...)— Recomputes code sizes after config/protocol upgrades.
LedgerSnapshot / CompleteConstLedgerState
LedgerSnapshot: Short-lived read-only snapshot over either SQL (via LedgerTxnReadOnly) or BucketList (via BucketSnapshotState). Should not persist across ledger boundaries. Provides getLedgerHeader(), getAccount(), load().
CompleteConstLedgerState: Immutable bundle of all ledger state at a specific sequence: BucketList snapshot, hot archive snapshot, Soroban config, ledger header, and HAS. Stored as the LCL in LedgerManagerImpl::mLastClosedLedgerState.
LedgerEntryWrapper / LedgerHeaderWrapper
Variant wrappers that unify LedgerTxnEntry/ConstLedgerTxnEntry/shared_ptr<LedgerEntry const> (and LedgerTxnHeader/shared_ptr<LedgerHeader>) behind a common read interface, used by the snapshot abstractions.
LedgerCloseMetaFrame
Wraps LedgerCloseMeta XDR. Built during applyLedger() to record per-transaction fee processing, tx execution meta, upgrade processing, evicted entries, and network configuration. Streamed to external consumers.
SharedModuleCacheCompiler
Multi-threaded producer-consumer for compiling Soroban Wasm contracts. A loader thread reads contracts from the BucketList snapshot and pushes Wasm blobs; N-1 compiler threads pop and compile them into the module cache. Uses condition variables for flow control with a byte-capacity buffer.
SorobanMetrics
Aggregates ledger-wide and per-tx Soroban resource usage metrics (CPU instructions, read/write bytes/entries, event sizes, host function execution times). Uses atomic counters for thread-safe accumulation during parallel apply.
LedgerEntryScope<S> / ScopedLedgerEntry<S>
Template-based compile-time + runtime scope-checking system for ledger entries. Prevents cross-thread and cross-phase access bugs.
Static scopes (enum StaticLedgerEntryScope): GlobalParApply, ThreadParApply, TxParApply, LclSnapshot, HotArchive, RawBucket.
Scope transitions (allowed adoptions): GlobalParApply ↔ ThreadParApply, ThreadParApply ↔ TxParApply.
Each scope has activation/deactivation to prevent stale reads from outer scopes. DeactivateScopeGuard provides RAII deactivation.
TrustLineWrapper / ConstTrustLineWrapper
Safe wrappers for trust line operations. Handles the special case of issuer accounts (which have no actual trust line entry but behave as if they have infinite balance). Provides getBalance(), addBalance(), getBuyingLiabilities(), getSellingLiabilities(), addBuyingLiabilities(), addSellingLiabilities(), isAuthorized(), etc.
RestoredEntries
Tracks entries restored during a ledger, in two categories: hotArchive (restored from hot archive BL, involves IO) and liveBucketList (restored from live BL where TTL expired but wasn't evicted, just TTL update). Maps LedgerKey → LedgerEntry.
LedgerRange / CheckpointRange
Simple half-open range types. LedgerRange(first, count) for ledger sequences. CheckpointRange(first, count, frequency) for history checkpoints, with methods to convert between checkpoint and ledger ranges.
Key Control Flow
Ledger Close (Normal Path)
- Herder calls
LedgerManager::valueExternalized()withLedgerCloseDatacontaining the consensus tx set. LedgerApplyManagerqueues the ledger and posts to the apply thread.- Apply thread calls
LedgerManagerImpl::applyLedger(): a. Finishes any pending Wasm compilation. b. Marks phaseAPPLYING. c. Opens aLedgerTxnonLedgerTxnRoot, loads header, validates tx set hash. d. Prefetches source account entries. e. Processes fees/seqnums (processFeesSeqNums): charges fees, increments sequence numbers. f. Applies transactions (applyTransactions): dispatches to sequential phase (classic) and parallel phase (Soroban). Classic txs are applied serially. Soroban txs are grouped into stages/clusters and executed in parallel on worker threads. g. Marks phaseCOMMITTING. h. Applies upgrades from SCP value. i. Seals the LedgerTxn, writes to BucketList and DB, runs invariants (sealLedgerTxnAndStoreInBucketsAndDB). j. Emits ledger close meta if streaming is enabled. k. Commits the SQL transaction. l. Starts background eviction scan for next ledger. m. Marks phaseREADY_TO_APPLY(end of committing). n. PostsadvanceLedgerStateAndPublishback to main thread (or calls directly if on main thread). - Main thread in
advanceLedgerStateAndPublish: a. Publishes any pending history checkpoint. b. GCs unreferenced buckets. c. Updates LCL state. d. Triggers next ledger in Herder.
Parallel Soroban Apply
Soroban transactions are organized into stages, each containing independent clusters of transactions. Within a stage:
applySorobanStagecreates aGlobalParallelApplyLedgerStatefrom the current LedgerTxn state.applySorobanStageClustersInParallelspawns worker threads, each runningapplyThreadon one cluster.- Worker threads execute transactions using read-only snapshots of ledger state. Changes are accumulated but not committed.
- After all threads complete, results are merged back by the primary apply thread.
LedgerTxn Commit Flow
When a LedgerTxn commits:
- If parent is another
LedgerTxn: entries frommEntryare merged into parent'smEntryviacommitChild. Parent'smWorstBestOfferandmRestoredEntriesare updated. - If parent is
LedgerTxnRoot: opens a SQL transaction, iterates all entries viaEntryIterator, applies bulk SQL operations (inserts to BucketList, upserts/deletes for offers in SQL), clears the entry cache.
Ownership Relationships
Applicationowns oneLedgerManagerImpland oneLedgerTxnRoot.LedgerManagerImplownsApplyState(which ownsInMemorySorobanState,SorobanModuleCache,SharedModuleCacheCompiler,LedgerApplyMetrics) andCompleteConstLedgerStatePtr(LCL state).LedgerTxnRootholds a reference toInMemorySorobanState(owned byApplyState), ownsEntryCache,BestOfferscache, and the SQLSessionWrapper.LedgerTxnholds a reference to its parent (AbstractLedgerTxnParent&) and owns itsImpl(which ownsEntryMap,ActiveMap,MultiOrderBook,WorstBestOfferMap).LedgerTxnEntryweakly references itsImpl(which references back to the owningLedgerTxn).CompleteConstLedgerStateowns immutable snapshots of BucketList, hot archive, Soroban config, header, and HAS.
Key Data Flows
- Consensus → Apply:
LedgerCloseData(tx set + hash) flows from Herder/SCP →LedgerApplyManager→ apply thread →LedgerManagerImpl::applyLedger(). - Apply → BucketList:
LedgerTxn::getAllEntries()extracts init/live/dead entries →BucketManager::addLiveBatch()andaddHotArchiveBatch(). - Apply → SQL:
LedgerTxnRoot::commitChild()writes offer changes to SQL via bulk operations;LedgerHeaderUtils::storeInDatabase()persists ledger headers. - Apply → InMemorySorobanState: After sealing the LedgerTxn,
ApplyState::updateInMemorySorobanState()applies init/live/dead entries to the in-memory Soroban cache. - Apply → Meta Stream:
LedgerCloseMetaFrameaccumulates per-tx meta during apply, then is emitted viamMetaStream. - Apply → Main Thread:
CompleteConstLedgerState(new LCL) is posted from apply thread to main thread viaadvanceLedgerStateAndPublish(). - Startup/Catchup → State:
loadLastKnownLedger()orsetLastClosedLedger()initializes LCL state, populatesInMemorySorobanStatefrom BucketList snapshot, and compiles the Soroban module cache.
> related_skills --same-repo
> validating-a-change
comprehensive validation of a change to ensure it is correct and ready for a pull request
> regenerating a technical summary of stellar-core
Instructions for regenerating the full set of subsystem and whole-system technical summary skill documents for stellar-core
> subsystem-summary-of-work
read this skill for a token-efficient summary of the work subsystem
> subsystem-summary-of-util
read this skill for a token-efficient summary of the util subsystem