Skip to main content

Purpose and Scope

This document describes the state management system in StableNet that maintains and manages the global state, including all account balances, nonces, contract code, and contract storage.
The state management layer provides a caching interface on top of a Merkle Patricia Trie structure, tracks state transitions that occur during transaction execution, and commits state changes at the block level.
Related documents:

StateDB Architecture

StateDB is the core interface responsible for accessing and modifying state. It is defined in core/state/statedb.go, provides an in-memory caching layer on top of the account trie, and manages the full lifecycle of state objects representing individual accounts. Key components:
  • trie: the account trie that stores account state
  • snaps: snapshot trees for fast state access (optional)
  • stateObjects: in-memory cache of account objects (map[common.Address]*stateObject)
  • journal: a journal that records state changes for rollback
  • originalRoot: the state root at the start of the current block execution
StateDB is created at the beginning of block execution with a specific state root, accumulates state changes while executing transactions within the block, and computes and commits a new state root at the end of the block.

State Objects

A stateObject is an internal structure that represents the state of a single account, including balance, nonce, code, and storage.
It is cached in memory and clearly distinguishes between modified (dirty) state and committed state.

StateAccount Structure

Each account’s persistent data is stored in the StateAccount structure defined in core/types/state_account.go:
FieldTypeDescription
Nonceuint64Transaction counter
Balance*uint256.IntAccount balance in Wei
Rootcommon.HashStorage trie root
CodeHash[]byteContract code hash
Extrauint64StableNet extension field (account policy flags)
The Extra field is a StableNet extension used to represent account policy state defined by the governance layer.

State Object Lifecycle

State objects are managed through the following stages:
  1. Create / Load: getStateObject() retrieves the object from cache or loads it from the trie or snapshot if absent
  2. Modify: balance, nonce, and storage values are updated in memory during transaction execution
  3. Finalize: on Finalise(), dirty state is moved to the pending state
  4. Commit: on Commit(), changes are written to the trie and persisted to the database

Trie Structure

State is managed using a hierarchical Merkle Patricia Trie structure:
  • Account Trie: a single trie storing the state of all accounts, keyed by address hash
  • Storage Trie: one per contract account, storing contract variable values
Key operations include:
OperationStateDB MethodDescription
Load accountgetStateObject()Load account state
Update accountupdateStateObject()Update account state
Delete accountdeleteStateObject()Remove account
Load storageGetState()Read storage slot
Update storageSetState()Update storage slot

Caching Layers

To reduce the cost of state access, a multi-level caching architecture is used:
Request → StateDB cache → Snapshot layer → Trie node cache → Disk database
  1. StateDB Cache
    Keeps stateObject instances in memory. It manages dirtyStorage modified in the current transaction, pendingStorage finalized but not yet committed, and originStorage for change tracking.
  2. Snapshot Layer
    Provides account and storage data in a flat key–value structure, bypassing trie traversal. This significantly improves read performance and is generated and updated asynchronously.
  3. Trie Node Cache
    Managed by the trie database, separating recently accessed clean nodes and modified (dirty) nodes not yet written to disk.
  4. Disk Database
    The final persistent storage, holding encoded trie nodes and state data.

Journaling and Snapshots

The journal system records all state changes sequentially, enabling transaction-level rollback.
If an error occurs during execution, all state changes made by that transaction can be precisely reverted.

Snapshots and Reverts

  • Snapshot(): creates a rollback checkpoint for the current state and returns a journal index
  • RevertToSnapshot(): restores all changes made after the specified snapshot in reverse order
The journal records the following types of changes:
  • Balance, nonce, and code updates
  • Storage slot updates
  • selfdestruct flags
  • Access list changes
  • Log additions
A snapshot is created before each EVM call, and if execution fails, only the state changes within that call scope are rolled back.

State Commit

State commits are performed at the block level and persist changes to permanent storage.

Commit Flow

Finalise → IntermediateRoot → Commit
1. Finalise
  • Move dirty storage to pending storage
  • Clean up empty accounts (EIP-161)
  • Called repeatedly after transaction execution
2. IntermediateRoot
  • Compute roots of all storage tries
  • Update the account trie
  • Return the current state root, used to compute the receipt root
3. Commit
  • Commit account and storage tries to disk
  • Update the trie database
  • Commit snapshots
  • Return the final state root

State Change Tracking

With the path-based state scheme (PBSS), the following tracking maps are maintained for efficient state management and pruning:
  • accounts / storages: accounts and storage entries modified in the current block
  • accountsOrigin / storagesOrigin: original values before modification
  • stateObjectsDestruct: information about deleted accounts
This information is passed to the trie database during commit and used for state management optimizations.

Database Integration

StateDB integrates with the trie database through the Database interface defined in core/state/database.go.
Both hash-based and path-based (PBSS) state schemes are supported.

Database Configuration

SettingDefaultDescription
StateSchemeHashSchemeState storage scheme
TrieCleanLimit256 MBClean cache size
TrieDirtyLimit256 MBDirty cache size
SnapshotLimit256 MBSnapshot cache size
PreimagesfalseWhether to store trie key preimages
StateHistory0Number of blocks to retain state history
Key interface methods:
  • OpenTrie(root)
  • OpenStorageTrie(stateRoot, addrHash, root, trie)
  • ContractCode(addrHash, codeHash)
  • TrieDB()

State Prefetching

State prefetching is an optimization technique that preloads required trie nodes during block execution.
It is started with StartPrefetcher() and stopped with StopPrefetcher().
The prefetcher predicts which accounts and storage slots transactions will access and loads them into cache in advance, reducing disk I/O wait time and improving block processing performance.

Performance Metrics

StateDB collects the following metrics to monitor state management performance:
  • Account and storage read/write/commit times
  • Snapshot lookup and commit times
  • Trie database commit times
  • Number of modified accounts and storage slots
These metrics help node operators analyze state processing bottlenecks and tune performance.