> subsystem-summary-of-simulation
read this skill for a token-efficient summary of the simulation subsystem
curl "https://skillshub.wtf/stellar/stellar-core/subsystem-summary-of-simulation?format=md"Simulation Subsystem — Technical Summary
Overview
The simulation subsystem provides infrastructure for creating multi-node network simulations and generating synthetic transaction load for testing and benchmarking stellar-core. It has four major components: (1) Simulation — orchestrates multiple Application instances as virtual or TCP-connected nodes, (2) Topologies — factory functions producing pre-configured network shapes, (3) LoadGenerator — drives sustained transaction submission at configurable rates, and (4) ApplyLoad — a closed-loop benchmarking harness that bypasses the overlay to measure raw ledger-close performance. A shared TxGenerator class constructs all transaction types (classic payments, Soroban uploads, invocations, SAC transfers, upgrades).
Key Files
- Simulation.h / Simulation.cpp —
Simulationclass; manages nodes, clocks, loopback/TCP connections, and event-loop cranking. - Topologies.h / Topologies.cpp — Static factory methods producing
Simulationinstances with specific network topologies. - LoadGenerator.h / LoadGenerator.cpp —
LoadGeneratorclass; timer-driven load submission loop, account management, metrics, completion tracking. - TxGenerator.h / TxGenerator.cpp —
TxGeneratorclass; creates all transaction types (payments, Soroban upload/invoke/upgrade, SAC, batch transfer). - ApplyLoad.h / ApplyLoad.cpp —
ApplyLoadclass; offline benchmarking: sets up contracts, populates bucket list, runs ledger-close iterations, binary-searches for maximum throughput.
Key Classes and Data Structures
Simulation
Orchestrates a multi-node stellar-core network within a single process. Used extensively in integration and consensus tests.
Enums:
Mode—OVER_LOOPBACK(in-process, virtual clocks) orOVER_TCP(real sockets, real clocks).
Type aliases:
ConfigGen—std::function<Config(int)>, generates per-node configs.QuorumSetAdjuster—std::function<SCPQuorumSet(SCPQuorumSet const&)>, post-processes quorum sets.QuorumSetSpec—std::variant<SCPQuorumSet, std::vector<ValidatorEntry>>, allows explicit or auto-generated quorum sets.
Key members:
mNodes(std::map<NodeID, Node>) — Maps node public keys toNodestructs. EachNodeowns aVirtualClockand anApplication::pointer.mPendingConnections(std::vector<std::pair<NodeID, NodeID>>) — Connections queued beforestartAllNodes().mLoopbackConnections(std::vector<std::shared_ptr<LoopbackPeerConnection>>) — Active in-process connections (loopback mode only).mClock(VirtualClock) — The simulation-level master clock.mIdleApp(Application::pointer) — A "background" application used for the master clock's IO context and timers.mPeerMap(std::unordered_map<unsigned short, std::weak_ptr<Application>>) — MapsPEER_PORTto application; enablesLoopbackOverlayManagerto resolve connection targets.mConfigGen— Optional config generator; defaults togetTestConfig().mQuorumSetAdjuster— Optional quorum-set postprocessor.mSetupForSorobanUpgrade(bool) — Flag indicating Soroban upgrade readiness.
Key functions:
addNode(SecretKey, QuorumSetSpec, Config*, bool)— Creates a newApplicationon its ownVirtualClock, registers it inmNodesandmPeerMap. Supports both explicitSCPQuorumSetand auto-generated quorum fromValidatorEntryvectors.addPendingConnection(NodeID, NodeID)— Queues a connection to be established whenstartAllNodes()runs.fullyConnectAllPending()— Adds pending connections between all pairs of nodes (mesh).startAllNodes()— Callsapp->start()on each node, then establishes all pending connections.stopAllNodes()— Gracefully stops all nodes and cranks until clocks stop.removeNode(NodeID)— Gracefully stops a node, drops all its connections, removes from maps.crankNode(NodeID, time_point)— Drives one node's clock forward by up to onequantum(100 ms) in virtual mode, or just catches up in real-time mode. Also updates the node's survey manager.crankAllNodes(int nbTicks)— Advances the entire simulation bynbTicksmeaningful events. Iterates all node clocks until they catch up to the master clock, then cranks the master clock.crankForAtMost / crankForAtLeast / crankUntil— Convenience wrappers that crank the simulation until a time limit or predicate is met.haveAllExternalized(uint32, uint32, bool)— Returns true if all (or validator-only) nodes have externalized at least ledgernum. Throws if spread exceedsmaxSpread.addConnection / dropConnection— Immediately create/drop a loopback or TCP connection.metricsSummary(string)— Dumps all metrics (or a domain) from the first node using a customConsoleReporterWithSum.
Simulation::Node (inner struct)
mClock(shared_ptr<VirtualClock>) — Per-node clock.mApp(Application::pointer) — The node's application. Destructor ensures app is destroyed before its clock.
LoopbackOverlayManager (extends OverlayManagerImpl)
Overrides connectToImpl to resolve the target from Simulation::mPeerMap and create a LoopbackPeer pair instead of opening a TCP socket.
ApplicationLoopbackOverlay (extends TestApplication)
A test application variant that creates LoopbackOverlayManager as its overlay and holds a back-reference to the owning Simulation.
Topologies
A static utility class with factory methods that create fully configured Simulation instances with common network shapes. All methods accept optional ConfigGen and QuorumSetAdjuster.
Factory methods:
pair(mode, networkID)— Two mutually-trusting validators, one connection.cycle4(networkID)— Four nodes in a cyclic quorum (loopback only), with cross-connections.core(nNodes, threshold, mode, networkID)— Fully-connected mesh ofnNodeswith shared quorum set.cycle(nNodes, threshold, mode, networkID)— Same quorum ascore, but connections form a one-way ring.branchedcycle(nNodes, ...)— Ring plus antipodal shortcuts.separate(nNodes, threshold, mode, networkID, numWatchers)— Shared quorum, no connections (callers add connections). Optionally includes watcher nodes.separateAllHighQuality(nNodes, mode, networkID, confGen)— Uses automatic quorum generation with all nodes markedVALIDATOR_HIGH_QUALITY.hierarchicalQuorum(nBranches, ...)— 4-node core plus mid-tier branches, connected round-robin.hierarchicalQuorumSimplified(coreSize, nbOuterNodes, ...)— Core plus outer nodes that trust core + self.customA(mode, networkID)— 7-node topology (A–S) for resilience tests; node I is dead.asymmetric(mode, networkID)— 10-nodecoretopology plus 4 extra nodes on one validator for asymmetry tests.
LoadGenMode (enum)
Defines the type of load the LoadGenerator produces:
PAY— Classic native payment transactions.SOROBAN_UPLOAD— Upload random Wasm blobs (overlay/herder testing).SOROBAN_INVOKE_SETUP— Deploy contracts for subsequentSOROBAN_INVOKE.SOROBAN_INVOKE— Invoke resource-intensive Soroban contracts.SOROBAN_UPGRADE_SETUP— Deploy the config-upgrade contract instance.SOROBAN_CREATE_UPGRADE— Submit a single config upgrade transaction.MIXED_CLASSIC_SOROBAN— Weighted blend ofPAY,SOROBAN_UPLOAD, andSOROBAN_INVOKE.PAY_PREGENERATED— Read pre-serialized payment transactions from an XDR file.SOROBAN_INVOKE_APPLY_LOAD— Generate invoke transactions matchingApplyLoad's V2 transaction shape.
GeneratedLoadConfig
Value struct parameterizing a load-generation run.
Key fields:
mode(LoadGenMode) — What type of transactions to generate.nAccounts,offset,nTxs,txRate— Account pool size, starting offset, total transactions, target tx/s.spikeInterval,spikeSize— Periodic traffic spikes.maxGeneratedFeeRate— When set, randomize fee rates up to this value.skipLowFeeTxs— If true, skip transactions rejected for low fee instead of failing.preloadedTransactionsFile— Path forPAY_PREGENERATEDmode.
Inner structs:
SorobanConfig—nInstances,nWasmscounts for Soroban setup modes.MixClassicSorobanConfig—payWeight,sorobanUploadWeight,sorobanInvokeWeightforMIXED_CLASSIC_SOROBAN.
Key methods:
isDone(),areTxsRemaining(),isLoad(),isSoroban(),isSorobanSetup()— State predicates.modeInvokes(),modeSetsUpInvoke(),modeUploads()— Check what sub-modes the current mode encompasses.getStatus()— Returns a JSON summary of the run.copySorobanNetworkConfigToUpgradeConfig(base, updated)— Copies diffs from Soroban network config intoSorobanUpgradeConfig.- Static factories:
createSorobanInvokeSetupLoad,createSorobanUpgradeSetupLoad,txLoad,pregeneratedTxLoad.
SorobanUpgradeConfig
Large struct of std::optional<> fields covering every Soroban network configuration parameter (compute, ledger access, bandwidth, state archival, parallel execution, SCP timing). Used by both LoadGenerator and ApplyLoad to construct on-chain config upgrade transactions.
LoadGenerator
Drives transaction submission against a live herder at a configurable rate. Created per-Application.
Key members:
mTxGenerator(TxGenerator) — Constructs all transaction types.mApp(Application&) — The application receiving transactions.mLoadTimer(unique_ptr<VirtualTimer>) — Fires everySTEP_MSECS(100 ms) to submit a batch of transactions.mStartTime— Timestamp when load generation began; used to compute cumulative target count.mTotalSubmitted(int64_t) — Running count of successfully submitted transactions.mAccountsInUse/mAccountsAvailable(unordered_set<uint64_t>) — Track source-account availability to avoid submitting transactions with pending source accounts.mContractInstanceKeys(UnorderedSet<LedgerKey>) — Persists across runs; holds deployed contract instance keys.mCodeKey(optional<LedgerKey>) — The Wasm code key fromSOROBAN_INVOKE_SETUP.mContactOverheadBytes(uint64_t) — Wasm size + overhead, used for resource estimation.mContractInstances(UnorderedMap<uint64_t, ContractInstance>) — Maps account IDs to their assigned contract instance (rebuilt each invoke run).mRoot(TestAccountPtr) — The root/genesis account.mFailed,mStarted(bool) — Run state flags.
Key functions:
generateLoad(GeneratedLoadConfig)— Main entry point. Callsstart()once, then runs the step loop: computestxPerStep, iterates creating and submitting transactions viasubmitTx(), decrementscfg.nTxs, and schedules the next step viascheduleLoadGeneration().start(cfg)— One-time initialization: populatesmAccountsAvailable, sets up contract instance mapping for invoke modes, opens preloaded transaction file if needed.scheduleLoadGeneration(cfg)— Validates configuration, checks protocol version, then schedulesgenerateLoadviamLoadTimerif the app is synced (otherwise waits 10 s and retries).submitTx(cfg, generateTx)— Calls the transaction-generator lambda, submits viaexecute(), retries up toTX_SUBMIT_MAX_TRIESontxBAD_SEQby refreshing sequence numbers.execute(txf, mode, code)— Submits a transaction to the herder viarecvTransaction(), records per-mode metrics, broadcasts on success.getTxPerStep(txRate, spikeInterval, spikeSize)— Computes total target transactions based on elapsed time and spike schedule, returns the delta since last submission.cleanupAccounts()— Moves accounts frommAccountsInUseback tomAccountsAvailableonce no longer pending in the herder.waitTillComplete(cfg)— After all transactions are submitted, waits up toTIMEOUT_NUM_LEDGERSfor account sequence numbers and Soroban state to sync with the database, then marks completion or failure.checkAccountSynced(app)— Compares cached account sequence numbers against the database.checkSorobanStateSynced(app, cfg)— Verifies contract instance and code keys exist in the ledger.checkMinimumSorobanSuccess(cfg)— Checks whether the configured minimum Soroban success percentage was met.stop()— Cancels the load timer and resets state.reset()— Clears per-run state (accounts, timer, counters) but preservesmContractInstanceKeysandmCodeKey.resetSorobanState()— Clears contract keys and code key (only on setup failures).
Control flow:
- External caller invokes
generateLoad(cfg). start()initializes accounts and contract mappings.- Each 100 ms step: compute target tx count, generate and submit transactions in a loop, decrement remaining count.
- When
nTxsreaches 0, switch towaitTillComplete()which polls per-ledger until DB state is consistent. - Mark
mLoadgenCompleteormLoadgenFailand callreset().
Constants:
STEP_MSECS = 100— Step interval.TX_SUBMIT_MAX_TRIES = 10— Max retries per transaction.TIMEOUT_NUM_LEDGERS = 20— Max ledgers to wait for completion.MIN_UNIQUE_ACCOUNT_MULTIPLIER = 3— Ensures enough unique accounts for sustained rate.
TxGenerator
Constructs transaction frames for all supported load types. Shared by LoadGenerator and ApplyLoad.
Key members:
mApp(Application&) — Application reference.mAccounts(std::map<uint64_t, TestAccountPtr>) — Account cache, keyed by numeric ID.mMinBalance(int64) — Cached minimum balance for account creation.mApplySorobanSuccess / mApplySorobanFailure(medida::Counter&) — Track Soroban apply outcomes.mPrePopulatedArchivedEntries/mNextKeyToRestore— State for simulating hot-archive disk reads in V2 load.
Key constants:
ROOT_ACCOUNT_ID = UINT64_MAX— Sentinel for root account.SAC_TX_INSTRUCTIONS = 250'000— Instructions per SAC transfer.BATCH_TRANSFER_TX_INSTRUCTIONS = 500'000— Instructions per batch transfer.
Transaction creation functions:
paymentTransaction(...)— Classic XLM payment of 1 stroop between two random accounts.createUploadWasmTransaction(...)— Uploads a Wasm blob viaHOST_FUNCTION_TYPE_UPLOAD_CONTRACT_WASM.createContractTransaction(...)— Instantiates a contract from an uploaded Wasm.createSACTransaction(...)— Creates a Stellar Asset Contract for a given asset.sorobanRandomWasmTransaction(...)— Uploads a random-sized Wasm blob with randomized resources sampled from config distributions.invokeSorobanLoadTransaction(...)— V1 invoke: callsdo_work(guestCycles, hostCycles, numEntries, kbPerEntry)on the loadgen contract. Resources (instructions, IO, tx size) are sampled from configurable distributions.invokeSorobanLoadTransactionV2(...)— V2 invoke: callsdo_cpu_only_work(guestCycles, hostCycles, eventCount)with separate RW entries and archived-entry auto-restore. Used byApplyLoad.invokeSACPayment(...)— Invokes SACtransferfunction between accounts.invokeBatchTransfer(...)— Invokes a batch-transfer contract that performs multiple SAC transfers in one transaction.invokeSorobanCreateUpgradeTransaction(...)— Writes config upgrade bytes into the upgrade contract.getConfigUpgradeSetFromLoadConfig(SorobanUpgradeConfig)— Reads current config settings from the ledger, applies deltas fromSorobanUpgradeConfig, serializes asConfigUpgradeSet.
Utility functions:
findAccount(id, ledgerNum)— Looks up or creates aTestAccountin the cache.createAccounts(start, count, ledgerNum, initial)— CreatescreateAccountoperations in bulk.createTransactionFramePtr(from, ops, maxFeeRate, byteCount)— Wraps operations into a signedTransactionFrame, optionally padded to a target byte count.generateFee(maxGeneratedFeeRate, opsCnt)— Generates a fee, optionally randomized up tomaxGeneratedFeeRate.loadAccount(account)— Refreshes an account's sequence number from the ledger.pickAccountPair(...)— Selects source and random destination accounts for payments.reset()— Clears the account cache.
ApplyLoadMode (enum)
LIMIT_BASED— Generate load within configured ledger limits, measure close time.FIND_LIMITS_FOR_MODEL_TX— Binary-search for max number of a "model" transaction that fits in target close time.MAX_SAC_TPS— Binary-search for max SAC transfer TPS, ignoring ledger limits.
ApplyLoad
Offline benchmarking harness. Bypasses overlay/herder flooding; directly closes ledgers with generated transaction sets.
Key members:
mApp(Application&),mMode(ApplyLoadMode),mRoot(TestAccountPtr).mTxGenerator(TxGenerator) — Owns its ownTxGenerator(separate fromLoadGenerator's).mNumAccounts(uint32_t) — Total test accounts, computed from config and mode.mUpgradeCodeKey,mUpgradeInstanceKey— Keys for the config-upgrade contract.mLoadCodeKey,mLoadInstance— Keys/instance for the loadgen contract (V2 invocations).mSACInstanceXLM— SAC contract instance for native XLM.mBatchTransferInstances(vector<ContractInstance>) — One batch-transfer contract per parallel cluster.mDataEntryCount,mDataEntrySize— Dimensions of pre-populated bucket-list data entries.- Utilization histograms:
mTxCountUtilization,mInstructionUtilization,mTxSizeUtilization,mDiskReadByteUtilization,mWriteByteUtilization,mDiskReadEntryUtilization,mWriteEntryUtilization.
Key functions:
execute()— Dispatches tobenchmarkLimits(),findMaxSacTps(), orfindMaxLimitsForModelTransaction().setup()— Master setup: loads root account, upgrades max tx set size, callssetupAccounts,setupUpgradeContract,setupLoadContract,setupXLMContract, optionallysetupBatchTransferContractsandsetupBucketList.setupAccounts()— CreatesmNumAccountstest accounts in batches viacloseLedger.setupUpgradeContract()— Uploads thewrite_bytesWasm, instantiates it; stores keys inmUpgradeCodeKey/mUpgradeInstanceKey.setupLoadContract()— Uploads thetest_wasm_loadgenWasm, instantiates it; stores inmLoadCodeKey/mLoadInstance.setupXLMContract()— Creates the native-asset SAC; stores inmSACInstanceXLM.setupBatchTransferContracts()— Uploads the SAC-transfer contract, creates one instance per cluster, funds each with XLM.setupBucketList()— Pre-populates the live and hot-archive bucket lists with synthetic contract data entries over simulated ledgers.closeLedger(txs, upgrades, recordUtilization)— Creates aTxSetfrom transactions, optionally records resource utilization metrics, then closes the ledger.benchmarkLimits()— RunsAPPLY_LOAD_NUM_LEDGERSiterations ofbenchmarkLimitsIteration, logs timing and utilization statistics.benchmarkLimitsIteration()— Generates classic payments + Soroban V2 invoke transactions up to scaled ledger limits, closes one ledger with utilization recording.findMaxLimitsForModelTransaction()— Binary-searches over tx count: for each candidate, callsupdateSettingsForTxCount, upgrades network config, runsbenchmarkLimitsIterationseveral times, checks if mean close time is withinAPPLY_LOAD_TARGET_CLOSE_TIME_MS.findMaxSacTps()— Binary-searches over TPS: generates SAC payment transactions, times just the application phase, finds maximum sustainable TPS.benchmarkSacTps(txsPerLedger)— RunsAPPLY_LOAD_NUM_LEDGERSSAC-payment ledgers, returns average close time.generateSacPayments(txs, count)— Generates SAC payment or batch-transfer transactions with unique destinations to avoid RW conflicts.upgradeSettings()— Applies the LIMIT_BASED upgrade config viaapplyConfigUpgrade.upgradeSettingsForMaxTPS(txsToGenerate)— Computes high-limit config for MAX_SAC_TPS mode.applyConfigUpgrade(config)— Creates an upgrade transaction, validates it, closes a ledger with the upgrade.updateSettingsForTxCount(txsPerLedger)— Computes rounded ledger limits for a given tx count, returns the config and actual max txs.warmAccountCache()— Loads all accounts into the BucketListDB cache.successRate()— Returns fraction of apply-time successes.getKeyForArchivedEntry(index)(static) — DeterministicLedgerKeyfor a pre-populated hot-archive entry.calculateRequiredHotArchiveEntries(mode, config)(static) — Estimates total hot-archive entries needed for disk-read simulation.
Key Data Flows
Simulation Node Lifecycle
Simulationconstructor creates a masterVirtualClockand an idleApplication.addNode()creates aNodewith its own clock and anApplicationLoopbackOverlay(orTestApplicationfor TCP mode).addPendingConnection()queues connections;startAllNodes()starts apps and establishes connections.crankAllNodes()drives the main clock and all node clocks forward in lockstep (virtual mode) or lets them run freely (real-time mode).- Tests call
haveAllExternalized()to check consensus progress.
LoadGenerator Transaction Flow
- Caller invokes
generateLoad(cfg)via the HTTP command interface or test code. start()initializes account pools and contract mappings.- Every 100 ms step,
generateLoadcomputes the target batch size, creates transactions via mode-specific lambdas callingTxGenerator, and submits each viaexecute()→Herder::recvTransaction(). - Successful transactions are broadcast to the overlay. Failed transactions (bad seq) trigger account reload and regeneration.
- After all transactions are submitted,
waitTillComplete()polls account/Soroban state consistency across ledger closes.
ApplyLoad Benchmark Flow
ApplyLoadconstructor callssetup()which creates accounts, deploys contracts, populates bucket lists, and applies config upgrades.execute()dispatches to the selected benchmark mode.- In
LIMIT_BASEDmode: each iteration generates a full ledger of mixed classic + Soroban transactions, callscloseLedger(), records utilization. - In
FIND_LIMITS_FOR_MODEL_TXmode: binary search adjusts ledger limits, runs multiple iterations per candidate, checks mean close time against target. - In
MAX_SAC_TPSmode: binary search over TPS, generates SAC payments (individual or batched), times the application phase.
Ownership Relationships
SimulationownsmNodes(map ofNodestructs), eachNodeowns aVirtualClockandApplication.SimulationownsmLoopbackConnections(shared pointers toLoopbackPeerConnection).SimulationownsmIdleAppfor the master clock's IO context.LoadGeneratoris owned byApplication(one per app). It owns aTxGenerator, aVirtualTimer, and the account/contract state.ApplyLoadis a standalone object created for benchmarking. It owns its ownTxGeneratorand all contract/account state.TxGeneratoris owned by eitherLoadGeneratororApplyLoad. It owns the account cache (mAccounts).GeneratedLoadConfigandSorobanUpgradeConfigare value types passed by copy through the load-generation pipeline.
Threading Model
- In
OVER_LOOPBACKmode all nodes share a single thread;crankAllNodes()advances node clocks sequentially. - In
OVER_TCPmode each node has a real-time clock;crankAllNodes()spins and sleeps. LoadGeneratorruns entirely on the main thread, driven byVirtualTimercallbacks.ApplyLoadruns synchronously on the main thread; it directly closes ledgers without involving the overlay or herder flooding.
> 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