What is state?
State is the persistent data of the blockchain: account balances, delegations, governance proposals, module parameters, and any other data that survives between blocks. When a transaction executes, modules update state. When a block is committed, that updated state becomes the starting point for the next block:The KVStore model
At its lowest level, the Cosmos SDK stores state as key-value pairs. Both keys and values are byte arrays. Modules encode structured data into those bytes using Protocol Buffers, and decode them back when reading. See Encoding and Protobuf for details on how modules serialize data into bytes. The following example shows a conceptual example of how the bank module stores balances:x/bank/types/keys.go for the actual implementation.
Each module owns its own namespace in the key-value store. Keys are defined by the module and typically begin with a byte prefix that distinguishes them from other module keys.
Multistore
A single module store is only part of the picture. At the application level, all module stores are committed together. Every module has its own KVStore, and all module stores are mounted inside a multistore that is committed as a single state root. A module can only read and write to its own store through its keeper. Access is gated by aStoreKey, which is a typed capability object registered at app startup. Modules that don’t hold the key cannot open the store.
This isolation follows an object-capabilities model:
- Modules cannot directly mutate another module’s state
- Cross-module interaction must go through exposed keeper methods
How state is stored (IAVL and commit stores)
Each module’s KVStore is backed by aCommitKVStore. See the store spec for more details.
In the current SDK store implementation described here, the Cosmos SDK uses IAVL, a versioned AVL Merkle tree.
IAVL gives every read and write of the tree O(log n) complexity, meaning the time to read or write a key scales with the height of the tree, not the total number of keys. It also versions state on each block commit, and produces deterministic root hashes that can be used to generate Merkle proofs for light clients.
Each block commit produces a new tree version with a new root hash:
App hash
The app hash is the cryptographic root hash of the application’s committed state. It summarizes all module stores together through theCommitMultiStore. Because every validator executes the same state transitions deterministically, they should all compute the same app hash for a given block.
Database backend
The IAVL tree does not store data in memory. It writes versioned nodes to a database backend, which is a key-value store on disk. The Cosmos SDK uses CometBFT’sdb package to abstract over the database implementation. The default backend is goleveldb. Other supported backends include PebbleDB, RocksDB, and memDB (in-memory, for testing).
The database backend is selected at node startup and configured in app.toml. Application code never interacts with it directly; the store layer owns that boundary.
Store types in the SDK
Beyond the base KVStore, the SDK provides several specialized store wrappers.CommitKVStore (persistent store)
TheCommitKVStore is the main persistent store backed by IAVL. It persists across blocks, produces versioned commits, and contributes to the app hash.
CacheMultiStore (transaction isolation)
Before executing each transaction, the Cosmos SDK’sBaseApp creates a CacheMultiStore — a cached, copy-on-write view of the multistore.
All writes during that transaction occur in this cached layer:
- If the transaction succeeds, changes are written to the underlying store.
- If the transaction fails, the cache is discarded and no state changes are committed.
Ephemeral store types
Transient stores are cleared at the end of each block. They are used for temporary per-block data such as counters or intermediate calculations, and do not affect the app hash. Memory stores survive block commits but reset when the node restarts — theirCommit() is a no-op and data is never written to disk. They are used for in-process caching of data that is expensive to recompute each block but does not need to survive a restart. Modules access them via MemoryStoreKey, mounted with MountMemoryStores in app.go.
| Store type | Survives block commit | Survives restart |
|---|---|---|
| Transient | No (cleared each block) | No |
| Memory | Yes | No |
| IAVL (CommitKVStore) | Yes | Yes |
Gas and trace store wrappers
All store accesses are wrapped with additional behavior by theGasKVStore and TraceKVStore wrappers.
GasKVStorecharges gas for each read and writeTraceKVStorelogs each store operation for debugging
Prefix store
A prefix store wraps a KVStore and automatically prepends a fixed byte prefix to every key. This lets keepers scope their reads and writes to a sub-namespace without manually constructing prefixed keys on every call.Collections API (typed state access)
In the Cosmos SDK, modules commonly use the collections API to define typed state access. Instead of manually constructing byte keys, modules define typed collections such as:collections.Item[T]collections.Map[K, V]collections.Sequence
collections/collections.go for the base interface definitions. For the full package guide, see Collections.
How modules access state
Modules do not interact with the multistore directly. Instead, each module defines a keeper that opens its KVStore through the executionContext it receives on each call. For details on how Context carries the store reference at runtime, see Execution Context.
A keeper typically holds:
- the module’s store key (an object-capability used to open the module’s
KVStorefromContext), - a Protobuf codec used to encode and decode values stored as bytes,
- references (interfaces) to other keepers the module depends on.
Genesis and chain initialization
Before the first block executes, the chain must start with an initial state called genesis, defined ingenesis.json. Genesis is the first write to the KVStores — it is how every module’s state exists before any transaction runs.
During InitChain, BaseApp calls each module’s InitGenesis to populate its store:
DefaultGenesis, ValidateGenesis, InitGenesis, ExportGenesis) and initialization ordering, see Intro to Modules and Transaction Lifecycle.
Next steps
For more information on stores, pruning strategies, and store configuration, see the store spec. For the full store interface definitions, seestore/types/store.go in the SDK source.
Because KV stores only hold raw bytes, modules must serialize structured data before writing it. The next section, Encoding and Protobuf, explains how the Cosmos SDK uses Protocol Buffers to encode that data deterministically, and why every validator must produce exactly the same bytes.