# How the Akash Simulation Framework Tests Module State Transitions

> Learn how the Akash simulation framework tests module state transitions by creating an in-memory blockchain, generating random transactions, and verifying determinism through export-import cycles.

- Repository: [Akash Network/node](https://github.com/akash-network/node)
- Tags: internals
- Published: 2026-02-24

---

**The Akash simulation framework validates module state transitions by spawning a full in-memory blockchain, generating weighted random transactions for every module, executing them through mock deliveries against the BaseApp, and verifying determinism via export-import cycles.**

The `akash-network/node` repository employs the Cosmos SDK simulation framework to fuzz-test state-transition logic across all Akash modules. This approach constructs a temporary LevelDB-backed application instance, feeds it randomized transaction sequences, and asserts that keeper mutations, event emissions, and store updates remain consistent and panic-free under chaotic workloads.

## Architecture of the Akash Simulation Framework

### Simulation Environment Setup

Every simulation test begins with `SetupSimulation` in [`testutil/sims/simulation_helpers.go`](https://github.com/akash-network/node/blob/main/testutil/sims/simulation_helpers.go). This helper initializes a disposable environment consisting of a temporary directory, a LevelDB instance, a structured logger, and a `simtypes.Config` object that carries the random seed and chain ID.

```go
config := sim.NewConfigFromFlags()
config.ChainID = "akash-sim"

dir, _ := os.MkdirTemp("", dirPrefix)
db, _   := dbm.NewDB(dbName, dbm.BackendType(config.DBBackend), dir)
logger  := log.NewNopLogger()

```

If the `FlagEnabledValue` disables simulation, the function returns early and the test skips. Otherwise, the returned `config`, `db`, `dir`, and `logger` are passed to the application constructor.

### Application Instantiation

The framework instantiates the Akash application exactly as a production node would, with two critical modifications for speed. In [`app/sim_test.go`](https://github.com/akash-network/node/blob/main/app/sim_test.go), the `TestFullAppSimulation` function calls `akash.NewApp` with `fauxMerkleModeOpt`, which forces BaseApp to use an in-memory “faux-Merkle” adapter, and sets the chain ID to `"akash-sim"`.

```go
appOpts := viper.New()
appOpts.Set("home", akash.DefaultHome)

app := akash.NewApp(
    logger, db, nil, true,
    sim.FlagPeriodValue, map[int64]bool{},
    encodingConfig, appOpts,
    fauxMerkleModeOpt, baseapp.SetChainID("akash-sim"),
)

```

This creates a fully functional app with all keepers and stores, ready to process simulated blocks without the I/O overhead of real Merkle proofs.

## How Modules Define State Transition Tests

### WeightedOperations Implementation

Each Akash module implements a `WeightedOperations` function that tells the simulator which messages to generate and how often. For example, in [`x/provider/simulation/operations.go`](https://github.com/akash-network/node/blob/main/x/provider/simulation/operations.go), the function returns weighted operations for `MsgCreateProvider` and `MsgUpdateProvider`.

```go
func WeightedOperations(
    appParams simtypes.AppParams,
    _ codec.JSONCodec,
    ak govtypes.AccountKeeper,
    bk bankkeeper.Keeper,
    k keeper.IKeeper,
) simulation.WeightedOperations {
    var weightMsgCreate, weightMsgUpdate int

    appParams.GetOrGenerate(OpWeightMsgCreate, &weightMsgCreate, nil,
        func(_ *rand.Rand) { weightMsgCreate = appparams.DefaultWeightMsgCreateProvider })
    appParams.GetOrGenerate(OpWeightMsgUpdate, &weightMsgUpdate, nil,
        func(_ *rand.Rand) { weightMsgUpdate = appparams.DefaultWeightMsgUpdateProvider })

    return simulation.WeightedOperations{
        simulation.NewWeightedOperation(weightMsgCreate, SimulateMsgCreate(ak, bk, k)),
        simulation.NewWeightedOperation(weightMsgUpdate, SimulateMsgUpdate(ak, bk, k)),
    }
}

```

The `SimulateMsgCreate` and `SimulateMsgUpdate` generators construct random valid messages, select random accounts, and prepare fees. Similar patterns exist in [`x/market/simulation/operations.go`](https://github.com/akash-network/node/blob/main/x/market/simulation/operations.go), [`x/deployment/simulation/operations.go`](https://github.com/akash-network/node/blob/main/x/deployment/simulation/operations.go), and other modules under `x/*/simulation`.

### Mock Transaction Delivery

Once a message is generated, the operation calls `deliverMockTx` (defined in the same [`operations.go`](https://github.com/akash-network/node/blob/main/operations.go) files). This helper builds a signed transaction using `simtestutil.GenSignedMockTx`, injects random fees, and executes it via `app.SimDeliver`.

```go
tx, err := simtestutil.GenSignedMockTx(
    r, txGen, []sdk.Msg{msg}, fees,
    simtestutil.DefaultGenTxGas, chainID,
    []uint64{acc.GetAccountNumber()}, []uint64{acc.GetSequence()}, privKey)
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)

```

If `SimDeliver` succeeds, the state transition— including all keeper writes, module hooks, and event emissions—is applied to the in-memory KV-stores. Any panic or error aborts the simulation, immediately surfacing bugs in the state-transition logic.

## Executing the Full Simulation Cycle

### Running SimulateFromSeed

The entry point `TestFullAppSimulation` in [`app/sim_test.go`](https://github.com/akash-network/node/blob/main/app/sim_test.go) invokes `simulation.SimulateFromSeed` from the Cosmos SDK. This function orchestrates the entire fuzz run.

```go
_, simParams, simErr := simulation.SimulateFromSeed(
    t,
    os.Stdout,
    app.BaseApp,
    simtestutil.AppStateFn(...),
    sdksim.RandomAccounts,
    simtestutil.BuildSimulationOperations(app, app.AppCodec(), config, app.TxConfig()),
    app.ModuleAccountAddrs(),
    config,
    app.AppCodec(),
)

```

`BuildSimulationOperations` aggregates the `WeightedOperations` from every module into a single slice. During execution, the simulator repeatedly selects a weighted operation, delivers its mock transaction, commits the block, and advances the random seed. If `config.Commit` is true, `sim.PrintStats(db)` dumps LevelDB statistics for debugging.

### Export and Import Validation

After the run completes, `CheckExportSimulation` (in [`testutil/sims/simulation_helpers.go`](https://github.com/akash-network/node/blob/main/testutil/sims/simulation_helpers.go)) optionally writes the final app state and simulation parameters to JSON files. The `TestAppImportExport` test then performs a determinism check:

1. Calls `app.ExportAppStateAndValidators` to produce a genesis JSON.
2. Starts a fresh simulation environment with `SetupSimulation`.
3. Bootstraps a new app instance using the exported genesis.

If the new app initializes without errors, the simulation confirms that state transitions are **deterministic** and that exported state can be replayed faithfully.

## Code Example: End-to-End Simulation Test

The following snippet demonstrates the complete flow used in [`app/sim_test.go`](https://github.com/akash-network/node/blob/main/app/sim_test.go) to validate the Akash simulation framework:

```go
// 1️⃣  Setup the simulation environment
config, db, dir, logger, skip, err := sim.SetupSimulation(
    "leveldb-app-sim", "Simulation")
if skip { t.Skip("simulation disabled") }
require.NoError(t, err)

// 2️⃣  Build the Akash app
encoding := sdkutil.MakeEncodingConfig()
app := akash.NewApp(
    logger, db, nil, true,
    sim.FlagPeriodValue, map[int64]bool{},
    encoding, viper.New(),
    fauxMerkleModeOpt, baseapp.SetChainID("akash-sim"),
)

// 3️⃣  Run the randomised simulation
_, simParams, simErr := simulation.SimulateFromSeed(
    t, os.Stdout,
    app.BaseApp,
    simtestutil.AppStateFn(app.AppCodec(),
        app.SimulationManager(),
        akash.NewDefaultGenesisState(app.AppCodec())),
    sdksim.RandomAccounts,
    simtestutil.BuildSimulationOperations(
        app, app.AppCodec(), config, app.TxConfig()),
    app.ModuleAccountAddrs(),
    config,
    app.AppCodec(),
)
require.NoError(t, simErr)

// 4️⃣  Export for inspection (optional)
err = simtestutil.CheckExportSimulation(app, config, simParams)
require.NoError(t, err)

// 5️⃣  Clean-up
defer func() {
    _ = db.Close()
    require.NoError(t, os.RemoveAll(dir))
}()

```

## Summary

The Akash simulation framework rigorously tests module state transitions by:

- **Creating a temporary simulation environment** with LevelDB and a fast in-memory Merkle adapter via `SetupSimulation` in [`testutil/sims/simulation_helpers.go`](https://github.com/akash-network/node/blob/main/testutil/sims/simulation_helpers.go).
- **Collecting weighted operations** from every module’s `WeightedOperations` function (e.g., [`x/provider/simulation/operations.go`](https://github.com/akash-network/node/blob/main/x/provider/simulation/operations.go)).
- **Delivering mock transactions** through `deliverMockTx` and `app.SimDeliver` to exercise keeper logic and store mutations.
- **Running randomized block production** via `simulation.SimulateFromSeed` to ensure stability under unpredictable workloads.
- **Validating determinism** through export-import cycles in `TestAppImportExport`, confirming that state can be faithfully replayed from genesis.

## Frequently Asked Questions

### What is the purpose of WeightedOperations in Akash module simulations?

**WeightedOperations** tells the simulator which messages a module can process and how frequently to generate them. Defined in files like [`x/provider/simulation/operations.go`](https://github.com/akash-network/node/blob/main/x/provider/simulation/operations.go), this function returns a slice of weighted operation generators (e.g., `SimulateMsgCreate`) that the Cosmos SDK simulator samples according to their assigned probabilities, ensuring diverse state-transition coverage.

### How does the framework ensure deterministic state transitions?

After a simulation run, the `TestAppImportExport` test in [`app/sim_test.go`](https://github.com/akash-network/node/blob/main/app/sim_test.go) exports the final state via `app.ExportAppStateAndValidators`, then instantiates a fresh application using that exported genesis. If the new app boots without errors, the test proves that the sequence of state transitions produced a deterministic, reproducible state that can be re-imported reliably.

### Which database backend does the Akash simulation framework use?

The framework uses **LevelDB** as the temporary storage backend. The `SetupSimulation` helper in [`testutil/sims/simulation_helpers.go`](https://github.com/akash-network/node/blob/main/testutil/sims/simulation_helpers.go) creates a LevelDB instance via `dbm.NewDB` inside a temporary directory, providing fast, persistent storage for the in-memory app’s KV-stores without affecting the host’s default data directory.

### Where are the simulation tests located in the Akash repository?

Simulation utilities and helpers reside in [`testutil/sims/simulation_helpers.go`](https://github.com/akash-network/node/blob/main/testutil/sims/simulation_helpers.go). Full-application simulation tests live in [`app/sim_test.go`](https://github.com/akash-network/node/blob/main/app/sim_test.go). Per-module simulation logic— including `WeightedOperations` and mock delivery functions—is located in `x/*/simulation/` directories (e.g., [`x/provider/simulation/operations.go`](https://github.com/akash-network/node/blob/main/x/provider/simulation/operations.go), [`x/market/simulation/operations.go`](https://github.com/akash-network/node/blob/main/x/market/simulation/operations.go)).