Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/missions.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,18 @@ Run a network with a mix of nodes running the old and new nomination leader elec
## MissionMixedNominationLeaderElectionWithNewMajority

Run a network with a mix of nodes running the old and new nomination leader election algorithms. Contains a majority of nodes running the new algorithm.

## MissionPubnetNetworkLimitsBench

This mission simulates the full pubnet topology and tests how the network performs under specific transaction size limits, currently targeting SLP-4. For future SLP evaluations, adjust mission's SLP_TX_SIZE_LIMIT and SLP_TX_SIZE_MULTIPLIER parameters as needed.

### Required Parameters

- `--tx-size-bytes`: **Required**. Specifies the distribution of transaction sizes to generate.
- `--tx-size-bytes-weights`: **Required**. Specifies the weights for each transaction size in the distribution.
- `--tx-rate`: Specifies the transaction rate to generate. 200 TPS will be dedicated to classic, the rest is for Soroban invokes. Default is 230 TPS.

Examples:
- `--tx-size-bytes 120000 --tx-size-bytes-weights 1` - Generate only large transactions (~120KB)
- `--tx-size-bytes 40000,120000 --tx-size-bytes-weights 1,1` - Generate a 50/50 mix of medium (~40KB) and large (~120KB) transactions
- `--tx-size-bytes 20000,80000,120000 --tx-size-bytes-weights 3,2,1` - Weighted distribution: 50% small, 33% medium, 17% large
1 change: 1 addition & 0 deletions src/FSLibrary/FSLibrary.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<Compile Include="MissionHistoryGenerateAndCatchup.fs" />
<Compile Include="MissionHistoryPubnetMinimumCatchup.fs" />
<Compile Include="MissionSimulatePubnetMixedLoad.fs" />
<Compile Include="MissionPubnetNetworkLimitsBench.fs" />
<Compile Include="MissionSlowNodesNetwork.fs" />
<Compile Include="MissionMaxTPSClassic.fs" />
<Compile Include="MissionHistoryPubnetRecentCatchup.fs" />
Expand Down
4 changes: 2 additions & 2 deletions src/FSLibrary/MaxTPSTest.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ open StellarSupercluster

// Get the maximum value from a distribution. Returns `None` if `distribution`
// is the empty list
let private maxDistributionValue (distribution: (int * int) list) =
let maxDistributionValue (distribution: (int * int) list) =
match distribution with
| [] -> None
| _ -> Some(fst (List.maxBy fst distribution))

// Get the max of two optional values.
let private maxOption (x: int option) (y: int option) =
let maxOption (x: int option) (y: int option) =
match x, y with
| Some x, Some y -> Some(max x y)
| Some x, None -> Some x
Expand Down
209 changes: 209 additions & 0 deletions src/FSLibrary/MissionPubnetNetworkLimitsBench.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// Copyright 2026 Stellar Development Foundation and contributors. Licensed
// under the Apache License, Version 2.0. See the COPYING file at the root
// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0

module MissionPubnetNetworkLimitsBench

// The point of this mission is to simulate pubnet as closely as possible under
// a mix of classic and soroban load.

open MaxTPSTest
open PubnetData
open StellarCoreSet
open StellarMissionContext
open StellarFormation
open StellarStatefulSets
open StellarNetworkData
open StellarSupercluster
open StellarCoreHTTP
open StellarCorePeer

let largeMultiplier = 100
// Note: SLP4-specific tx size limits, update as needed
let SLP_TX_SIZE_LIMIT = 132_096
let PRE_SLP_TX_SIZE_LEDGER_LIMIT = 133_120
let POST_SLP_TX_SIZE_LEDGER_LIMIT = PRE_SLP_TX_SIZE_LEDGER_LIMIT * 2

let upgradeSorobanTxLimits (context: MissionContext) (formation: StellarFormation) (coreSetList: CoreSet list) =
formation.SetupUpgradeContract coreSetList.Head

let instructions =
Option.map (fun x -> (int64 x * int64 largeMultiplier)) (maxDistributionValue context.instructionsDistribution)

let txBytes =
Option.map ((*) (largeMultiplier * 1024)) (maxDistributionValue context.totalKiloBytesDistribution)

let entries =
Option.map ((*) largeMultiplier) (maxDistributionValue context.dataEntriesDistribution)

let txSizeBytes = Some(SLP_TX_SIZE_LIMIT)
let wasmBytes = Some(SLP_TX_SIZE_LIMIT)

formation.DeployUpgradeEntriesAndArm
coreSetList
{ LoadGen.GetDefault() with
mode = CreateSorobanUpgrade
txMaxInstructions = instructions
txMaxReadBytes = txBytes
txMaxWriteBytes = maxOption txBytes wasmBytes
txMaxReadLedgerEntries = entries
txMaxWriteLedgerEntries = entries
txMaxFootprintSize = entries
txMaxSizeBytes = txSizeBytes
maxContractSizeBytes =
Option.map ((*) largeMultiplier) (maxDistributionValue context.wasmBytesDistribution)
// Memory limit must be reasonably high
txMemoryLimit = Some 200000000 }
(System.DateTime.UtcNow)

let peer = formation.NetworkCfg.GetPeer coreSetList.Head 0

match instructions with
| Some instructions -> peer.WaitForTxMaxInstructions instructions
| None ->
// Wait a little
peer.WaitForFewLedgers 3

// Multiplier to use when increasing ledger limits. Expected
// ledger close time (5 seconds) multiplied by some factor to add headroom (2x)
let private ledgerSecondsMultiplier = 5 * 2

let upgradeSorobanLedgerLimits
(context: MissionContext)
(formation: StellarFormation)
(coreSetList: CoreSet list)
(txrate: int)
=
formation.SetupUpgradeContract coreSetList.Head

// Make the multiplier for all limits exception txSize really large to only surge on tx size
let allOtherLimitsMultiplier = txrate * ledgerSecondsMultiplier * largeMultiplier

let instructions =
Option.map
(fun x -> (int64 x * int64 allOtherLimitsMultiplier))
(maxDistributionValue context.instructionsDistribution)

let txBytes =
Option.map ((*) (allOtherLimitsMultiplier * 1024)) (maxDistributionValue context.totalKiloBytesDistribution)

let entries =
Option.map ((*) allOtherLimitsMultiplier) (maxDistributionValue context.dataEntriesDistribution)

// Note: pass the desired network limit here. Multiplier is not needed in order to enforce the limit to test.
let txSizeBytes = Some(POST_SLP_TX_SIZE_LEDGER_LIMIT)

let wasmBytes = txSizeBytes

formation.DeployUpgradeEntriesAndArm
coreSetList
{ LoadGen.GetDefault() with
mode = CreateSorobanUpgrade
ledgerMaxInstructions = instructions
ledgerMaxReadBytes = txBytes
ledgerMaxWriteBytes = maxOption txBytes wasmBytes
ledgerMaxTxCount = Some allOtherLimitsMultiplier
ledgerMaxReadLedgerEntries = entries
ledgerMaxWriteLedgerEntries = entries
ledgerMaxTransactionsSizeBytes = maxOption txSizeBytes wasmBytes }
(System.DateTime.UtcNow)

let peer = formation.NetworkCfg.GetPeer coreSetList.Head 0
peer.WaitForLedgerMaxTxCount allOtherLimitsMultiplier

let pubnetNetworkLimitsBench (baseContext: MissionContext) =
let context =
{ baseContext with
numAccounts = 30000
coreResources = SimulatePubnetResources
genesisTestAccountCount = Some 30000
// As the goal of `SimulatePubnet` is to simulate a pubnet,
// network delays are, in general, indispensable.
// Therefore, unless explicitly told otherwise, we will use
// network delays.
installNetworkDelay = Some(baseContext.installNetworkDelay |> Option.defaultValue true)
enableTailLogging = false
byteCountDistribution = defaultListValue pubnetByteCounts baseContext.byteCountDistribution
wasmBytesDistribution = defaultListValue pubnetWasmBytes baseContext.wasmBytesDistribution
dataEntriesDistribution = defaultListValue pubnetDataEntries baseContext.dataEntriesDistribution
totalKiloBytesDistribution = defaultListValue pubnetTotalKiloBytes baseContext.totalKiloBytesDistribution

// Note: try different distributions here to benchmark different transactions profiles
// e.g. [120_000, 1] for large txs only, or
// [(40_000, 1); (120_000, 1)] for a mix of medium and large txs
txSizeBytesDistribution =
match baseContext.txSizeBytesDistribution with
| [] -> failwith "PubnetNetworkLimitsBench mission requires tx size distribution to be set"
| _ -> baseContext.txSizeBytesDistribution

instructionsDistribution = defaultListValue pubnetInstructions baseContext.instructionsDistribution

// Unless otherwise specified, cap max connections at 65 for
// performance.
maxConnections = Some(baseContext.maxConnections |> Option.defaultValue 65)

updateSorobanCosts = Some(true)

// Transactions per second. Default is ~200 payment TPS and ~30 invoke load TPS.
txRate = if baseContext.txRate > 200 then baseContext.txRate else 230

skipLowFeeTxs = true }

let fullCoreSet = FullPubnetCoreSets context true false

let tier1 = List.filter (fun (cs: CoreSet) -> cs.options.tier1 = Some true) fullCoreSet

let nonTier1 = List.filter (fun (cs: CoreSet) -> cs.options.tier1 <> Some true) fullCoreSet

let numLoadGenerators = 20
let loadGenerators = List.take (min numLoadGenerators (List.length nonTier1)) nonTier1


let loadGen =
{ LoadGen.GetDefault() with
mode = MixedClassicSoroban
offset = 0
maxfeerate = None
skiplowfeetxs = context.skipLowFeeTxs
accounts = context.numAccounts
wasms = context.numWasms
instances = context.numInstances
txrate = context.txRate

// ~10 minutes of load
txs = context.txRate * 60 * 10

// Weights: 200 TPS and (txRate - 200) TPS
payWeight = Some(200 * 5)
sorobanInvokeWeight = Some((context.txRate - 200) * 5)
sorobanUploadWeight = Some(0)

// Require all Soroban transactions to succeed.
minSorobanPercentSuccess = Some(100) }

context.Execute
fullCoreSet
None
(fun (formation: StellarFormation) ->
// Setup overlay connections first before manually closing
// ledger, which kick off consensus
formation.WaitUntilConnected fullCoreSet
formation.ManualClose tier1

// Wait until the whole network is synced before proceeding,
// to fail asap in case of a misconfiguration
formation.WaitUntilSynced fullCoreSet

// Setup
formation.UpgradeProtocolToLatest tier1
formation.UpgradeMaxTxSetSize tier1 (context.txRate * 10)

upgradeSorobanLedgerLimits context formation tier1 context.txRate
upgradeSorobanTxLimits context formation tier1

for lg in loadGenerators do
// Run setup on nodes one at a time
formation.RunLoadgen lg context.SetupSorobanInvoke

formation.RunMultiLoadgen loadGenerators loadGen
formation.EnsureAllNodesInSync fullCoreSet)
2 changes: 2 additions & 0 deletions src/FSLibrary/StellarMission.fs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ open MissionSorobanCatchupWithPrevAndCurr
open MissionMixedImageNetworkSurvey
open MissionMaxTPSMixed
open MissionSimulatePubnetMixedLoad
open MissionPubnetNetworkLimitsBench
open MissionMixedNominationLeaderElection
open MissionUpgradeSCPSettings
open MissionUpgradeTxClusters
Expand Down Expand Up @@ -90,6 +91,7 @@ let allMissions : Map<string, Mission> =
("MixedImageNetworkSurvey", mixedImageNetworkSurvey)
("MaxTPSMixed", maxTPSMixed)
("SimulatePubnetMixedLoad", simulatePubnetMixedLoad)
("PubnetNetworkLimitsBench", pubnetNetworkLimitsBench)
("MixedNominationLeaderElectionWithOldMajority", mixedNominationLeaderElectionWithOldMajority)
("MixedNominationLeaderElectionWithNewMajority", mixedNominationLeaderElectionWithNewMajority)
("UpgradeSCPSettings", upgradeSCPSettings)
Expand Down