Introduction
SeiDB is a specialized database system designed to optimize blockchain state storage for the Ethereum Virtual Machine (EVM). It addresses fundamental performance constraints in traditional blockchain storage systems through targeted optimizations for EVM’s specific state access patterns. This document explains the technical design and key components of SeiDB.Core Technical Design
Traditional blockchain databases store state in structures optimized for cryptographic verification rather than transaction execution speed. SeiDB uses a hybrid architecture that preserves cryptographic verifiability while accelerating state access operations. Key design goals include:- Minimizing storage slot access latency even at peak load
- Maximizing state operation throughput for both reads and writes
- Enabling parallel execution for non-conflicting state operations
- Maintaining consistent performance under variable workloads
System Architecture
SeiDB implements a multi-layered architecture optimized for EVM state management: SeiDB CoreEVM Cache System
Query Processor
Storage Engine
Merkle Trie Optimizer
Storage Indexer
LSM-Tree Manager
Concurrency Control
Version Manager
I/O Scheduler
Ethereum Compatibility Layer
EVM-Optimized Storage Engine
The storage engine forms the foundation of SeiDB and represents its most significant departure from traditional blockchain state databases. While standard Ethereum implementations use a single Merkle Patricia Trie for all storage, SeiDB employs a hybrid approach combining cryptographic verification with performance optimizations from modern database systems. Key technical innovations include:- Enhanced Merkle Patricia Trie: The implementation preserves cryptographic properties required for consensus validation while addressing performance bottlenecks. SeiDB’s node caching dramatically reduces I/O overhead. Hot nodes remain in memory through a priority retention system that analyzes access frequency and recency patterns across multiple blocks.
- Incremental State Root Calculation: The system employs specialized techniques that avoid recalculating entire trie branches when only leaf nodes change. This method accelerates block finalization for blocks with transactions affecting different state areas. The calculation process intelligently reuses intermediate hash values from unchanged subtrees, allowing rapid state root derivation even after thousands of storage modifications.
- Direct Storage Slot Indexing: SeiDB provides mapping between composite keys (address + slot) and their storage location. This technique reduces lookup complexity from O(log n) to near-constant time for most operations. The indexing system maintains consistency through a dual-update mechanism that modifies both the index and underlying trie atomically.
- Account-Level Optimizations: The system applies different strategies to external accounts (user wallets) and contract accounts. Contract accounts receive specialized treatment with code caching and execution context preservation. The code caching mechanism exploits the immutability of contract bytecode once deployed, keeping frequently accessed contracts in memory with custom deserialization to minimize runtime overhead.
- Optimized Bloom Filters: The storage engine accelerates negative lookups (checking for non-existent keys) during contract execution. These filters use multi-layer filtering with dynamic sizing based on the active working set to minimize false positives during typical workloads.
Multi-Level Cache Architecture
The caching system implements multiple specialized caches optimized for particular EVM access patterns, unlike general-purpose databases that employ uniform caching strategies. The system includes:- Hot Slot Cache: This component maintains frequently used storage slots in memory using a frequency-recency hybrid eviction policy tuned for blockchain workloads. This adaptive approach achieves improved hit rates compared to static caching policies. The cache intelligently separates frequently accessed slots from burst-access slots to prevent cache thrashing during high-intensity operations.
- Account State Cache: This cache maintains complete information for recently accessed addresses, including code, balance, nonce, and metadata. It implements predictive loading based on transaction analysis for improved hit rates during smart contract interactions. The predictive engine analyzes calldata patterns and historical interaction graphs to preload likely-to-be-accessed contract accounts.
- Execution Context Cache: This specialized cache preserves partial execution environments for frequently called contracts. When the same contract executes repeatedly with similar call patterns, this contextual caching reduces setup overhead compared to cold execution. The context includes pre-validated jump destinations, resolved address references, and warmed storage slots.
Concurrency Management
SeiDB’s concurrency control system employs optimistic concurrency control (OCC) adapted specifically for EVM’s state access patterns. The system incorporates semantic knowledge of common smart contract behavior to minimize conflicts. The transaction execution process follows these steps:- Analysis of transaction targets, calldata patterns, and historical access data to create an initial dependency graph
- Parallel execution of transactions without overlapping state dependencies in isolated worker threads
- Monitoring of actual storage accesses during execution to identify conflicts against predictions
- Selective reexecution of minimal conflict sets in sequential order when conflicts occur
I/O Optimization
SeiDB implements storage I/O optimizations designed specifically for blockchain workloads, which typically involve append-heavy state changes. Key optimization techniques include:- Log-Structured Storage: The system organizes state into multiple levels, with recent changes in memory and older state in progressively larger but slower storage tiers. This architecture transforms random writes into sequential operations, improving write throughput. The storage layer maintains a memory-resident delta table that captures recent modifications and periodically flushes these changes to persistent storage in optimized batches.
- Priority-Based I/O Scheduling: The I/O subsystem prioritizes operations based on critical path status. State reads required for transaction validation receive highest priority, followed by state updates, and background operations receive lowest priority. The scheduler also employs operation batching techniques that combine multiple small I/O operations into more efficient larger operations.
- State Versioning: SeiDB uses multi-version concurrency control designed for blockchain’s block-based execution model. Each block creates a new state version, using full state snapshots at epoch boundaries and delta encoding between intermediate blocks. The versioning system enables point-in-time queries against historical state with minimal storage overhead through a combination of differential storage and periodic compaction.
- Configurable Persistence: The database offers adjustable durability guarantees based on node type and network requirements, from fully synchronous writes to asynchronous persistence with periodic checkpoints. The configuration system allows operators to make explicit tradeoffs between performance and durability based on their specific node’s role in the network.
Descending-Version MVCC Encoding (State Store)
SeiDB’s PebbleDB-backed state store uses multi-version concurrency control (MVCC) to keep every historical version of a key. Each logical key is stored on disk with its version appended, so a single key may have many versioned entries. The order in which those versions are laid out on disk directly affects how quickly the store can serve the most common query: reading the latest version of a key. Descending-version encoding for fresh databases. Newly created state stores now encode the version component of each MVCC key in descending byte order, so that newer versions of a logical key sort before older ones on disk. Because the newest visible version sits first, a latest-version read lands directly on the target entry via a single forward seek (First() / SeekGE) instead of scanning past older versions. This is the fast path and delivers faster latest-version reads for validators and API nodes that overwhelmingly query recent state.
Transparent compatibility with legacy databases. State stores written by the previous build used ascending-version encoding, where older versions sort first. To avoid forcing a migration, SeiDB detects the on-disk encoding when a database is opened and reads legacy stores using the ascending-version path automatically — no error is raised and no data conversion occurs. Encoding mode is fixed for the lifetime of an open database.
How detection works. Fresh databases are stamped with an on-disk sentinel key (s/_mvcc_descending) the first time they are opened, marking them as descending. On subsequent opens:
- If the sentinel is present, the database opens in descending (fast-path) mode.
- If the sentinel is absent but the database already contains data (a legacy database written by the previous ascending-version build), it opens in ascending (legacy) mode and is intentionally left unmarked.
- If both the sentinel and existing data are absent, the database is treated as fresh: the sentinel is written and descending mode is used.
UseDefaultComparer and iteration. The UseDefaultComparer field of the state store configuration influences how the descending-mode iterator advances to the next logical key. When enabled, the iterator falls back to a scan-based approach to locate the next logical key rather than using the MVCC comparer’s key-successor logic. This affects iterator advancement behavior only in descending mode.
Performance Characteristics
SeiDB is designed to deliver substantial performance improvements compared to traditional EVM state implementations. The architecture focuses on enhancing both throughput and latency across various operation types.Note: Performance characteristics described in this section represent design targets rather than verified benchmarks. Actual performance will vary based on hardware configuration, workload patterns, and network conditions. Production deployments should conduct their own benchmarking to validate performance in their specific environment.
Storage Operation Throughput
SeiDB is architected to significantly improve operation throughput across all major storage operation categories, particularly for storage reads and account lookups. These improvements result from architectural innovations rather than hardware scaling, with performance gains across all operation types.Latency Profile
The system is designed to maintain consistent low latency across different load conditions, from low to peak usage. This latency stability represents one of SeiDB’s most significant advantages for applications requiring predictable performance. The system aims to maintain relatively stable response times even at high utilization levels, unlike traditional implementations that may exhibit severe latency spikes during high network activity.Optimization Patterns
Understanding certain storage access patterns allows developers to take maximum advantage of SeiDB’s architecture. While existing contracts work without modification, those designed with these patterns achieve even greater performance.Localized Storage Access
SeiDB’s caching mechanisms work most effectively when related data exists in localized regions:Contention Reduction
Smart contracts handling high transaction volumes benefit from storage designs that minimize contentious storage locations:Technical Integration
EVM Compatibility
SeiDB maintains complete compatibility with the Ethereum protocol specifications while delivering performance enhancements:- Full support for all EVM opcodes and precompiled contracts
- Identical state transition logic to standard Ethereum implementations
- Consistent gas cost model for all operations
- Complete compatibility with JSON-RPC API endpoints
Deployment Configurations
SeiDB’s architecture supports various deployment configurations optimized for different node roles:- Validator nodes prioritize state consistency and durability through synchronous I/O operations and redundant state verification
- API service nodes optimize for query throughput and low latency responses with larger cache allocations and specialized read paths
- Archive nodes employ specialized storage strategies for efficient historical state access, including custom indexing for time-based queries
- Light clients benefit from optimized state proof generation with compact inclusion proofs for partial state verification
Profiling State Access with trace-profile-report
The seidb tool includes an offline trace-profile-report command for analyzing where time is spent while executing historical transactions. It runs the debug_traceTransactionProfile JSON-RPC method against a running node across a range of blocks and produces detailed timing and store-access reports. This is useful for identifying transactions and modules that dominate execution time or that generate heavy historical database lookups.
For each transaction in the requested block range, the command captures a full trace along with a profile that breaks down total execution time into per-phase timings (transaction lookup, block loading, historical transaction replay, block-context construction, transaction preparation, execution, and trace-result assembly) and per-module KVStore access statistics (Get/Has/Set/Delete counts and durations, plus iterator samples).
Usage
Flags
Output
The command writes two files to the output directory:raw_profiles.jsonl— one JSON line per transaction, containing the block number, block hash, transaction hash, and either the full trace-profile result or an error.summary.json— an aggregated report including total/success/error counts, average and P50/P95 latencies for total and historical-database-lookup time, per-phase totals, per-module store-access totals, and the top transactions and blocks by total execution time.
Because
trace-profile-report replays historical transactions, point it at a node (such as an archive node) that retains the historical state for the block range you want to profile.Dumping FlatKV State with dump-flatkv
The seidb tool includes a dump-flatkv command that iterates a FlatKV store and dumps every physical (key, value) pair into per-bucket files. FlatKV physical keys are grouped into four logical buckets — account, code, storage, and legacy — and each bucket is written to its own file inside the output directory. The output is formatted to match dump-iavl so the same diff tooling works on both dumps.
Each output file begins with a header line (Bucket <name> at version <V>) followed by one Key: <HEX>, Value: <HEX> line per physical row. Physical keys are emitted verbatim, including their <module>/ and type-prefix header. The FlatKV metadata rows are intentionally excluded, as they are internal bookkeeping.
Under the hood, the tool clones the selected FlatKV snapshot and changelog into a temporary directory and opens that isolated copy, so it never contends for the FlatKV writer lock on a live node.
Usage
Flags
Analyzing FlatKV with state-size
The state-size command now folds an optional FlatKV analysis into its output alongside the existing memIAVL module breakdown. When a FlatKV directory is present and --module is empty or evm, the tool scans FlatKV, reports a per-DB size breakdown (account, code, storage, legacy) and a table of the top EVM contracts by storage size, and — when exporting — includes the FlatKV row in the same DynamoDB batch as the memIAVL module rows.
Use the new --flatkv-dir flag to point at the FlatKV data directory. When it is not set, the tool auto-detects a sibling flatkv/ directory next to --db-dir (for example, <home>/data/committer.db → <home>/data/flatkv), which is the standard layout on a Sei node. If no such directory exists, FlatKV analysis is skipped and only the memIAVL modules are reported.
FlatKV analysis is strictly additive: if the FlatKV directory is missing or the store cannot be opened, the tool logs the reason and continues with the memIAVL analysis.
Reading the Latest memIAVL Version with memiavl-latest-version
The memiavl-latest-version command prints the latest committed memIAVL version of a stopped node. It is the read-only companion to import-flatkv-from-memiavl: an orchestration script can read each validator’s version after stopping seid, pick a single common height across a multi-validator cluster, and use that as the import height.
Usage
Flags
The command prints a single integer — the latest memIAVL version — to standard output.
Importing memIAVL Modules into FlatKV with import-flatkv-from-memiavl
The import-flatkv-from-memiavl command performs an offline import of selected memIAVL modules into FlatKV. It is used when migrating the EVM module’s state-commit (SC) layer from memIAVL to FlatKV storage. The command reads the selected module data from memIAVL at a target height, translates it into FlatKV’s on-disk layout, and bulk-imports it into the FlatKV store.
EVM-only initial scope. The initial production scope is intentionally narrow — only the evm module is accepted. Non-EVM modules remain in memIAVL and are not copied into FlatKV; passing any other module name is rejected at the CLI boundary.
Usage
Flags
Height constraints
The import must be run at the memIAVL latest height. The command refuses to import at a heightH below the memIAVL latest version, because a subsequent GIGA_STORAGE startup would call reconcileVersions and silently roll memIAVL back to H, truncating every cosmos block in (H, latest]. Operators who genuinely want a non-latest height must first roll memIAVL back to that height themselves — this command deliberately does not perform a destructive cosmos rollback on their behalf. A height ahead of the memIAVL latest version is likewise rejected.
If the import is interrupted (for example by context cancellation or an exporter/translator failure), the in-progress import is aborted rather than finalized: the FlatKV directory is left at its pre-import committed version, so the operation can be retried without
--force.Migration configuration constraints
When restarting a node after the import, keepevm-ss-split = false across the import boundary. The import moves only the EVM module’s SC-layer data into FlatKV; the EVM state-store history stays in the existing combined cosmos store, so enabling evm-ss-split would trigger a startup panic.
There is no longer any
sc-enable-lattice-hash setting to manage. That configuration field has been removed; whether the FlatKV lattice hash participates in the AppHash is now derived automatically from the node’s write mode and migration state, so operators do not need to toggle it across the import boundary.Polling FlatKV EVM Migration Status with migrate-evm-status
The migrate-evm-status command reports the on-disk FlatKV EVM migration state of a FlatKV directory as JSON. It exists so an orchestration script driving the in-flight migrate_evm migration can poll “is the migration done yet?” against each validator’s data directory from the host — without adding a custom RPC handler or grepping through node logs.
The command reads two reserved keys from the FlatKV migration store:
migration-version— an 8-byte big-endianuint64written exactly once per migration lifecycle, on the block that finalizes the migration. Absent or zero means the EVM migration has not yet completed.migration-boundary— the in-flight cursor encoding the(module, key)pair the next batch should resume from. It is present only while the migration is strictly between not-started and complete.
seidb tools, the read goes through the same read-only path used by dump-flatkv: the tool hardlink-clones the latest snapshot and copies the WAL into a temporary directory before opening. This avoids contending with a live node for the FlatKV writer lock and yields a stable view even if the live writer rolls snapshots mid-run, so the command can be run against a running validator.
Usage
Flags
Output
The command prints a single JSON object to standard output:A migration is complete when
migrate_evm_complete is true and boundary_present is false. Poll every validator until all report completion before flipping sc-write-mode from migrate_evm to evm_migrated.Comparing Backends with evm-logical-digest
The evm-logical-digest command computes a backend-independent digest of the EVM logical state (the canonical account, code, and storage buckets) so a memIAVL node and a FlatKV node can be compared at the same chain height. Because the two backends store the same EVM state in different physical layouts, a naive byte-for-byte comparison diverges: every FlatKV value embeds a per-key blockHeight stamp recording when the key was last written or migrated, and a freshly migrated FlatKV node stamps migration-time heights that differ from the memIAVL leaf versions. This tool strips the serialization-version and blockHeight header on both sides and digests only the height-independent logical payload (storage word, bytecode, and balance/nonce/codeHash), so identical EVM state produces identical digests regardless of backend.
Each bucket is accumulated as an order-independent XOR of sha256(len(key) || key || len(val) || val), so it does not matter that FlatKV iterates in Pebble global order while memIAVL is scanned by leaf index. The command prints a bucket_digest line per bucket and a single FINAL_DIGEST line covering account+code+storage+legacy; the two backends’ FINAL_DIGEST values should match when the underlying state is identical.
The legacy bucket is reported separately, along with a marker-adjusted comparison line, because a migrated FlatKV node can contain a FlatKV-only migration/migration-version row that a memIAVL-only node never owns. That row is folded into the legacy bucket but omitted from the final comparison so the two sides line up apples-to-apples.
Normalization modes
For the memIAVL backend,--memiavl-normalization selects how raw EVM leaves are turned into logical buckets:
semantic(default, also accepted asindependent) — independently decodes raw EVM keys and values into the sameaccount/code/storage/legacybuckets without callingflatkv.ImportTranslator.translator— feeds each EVM leaf throughflatkv.ImportTranslator, applying the exact sameclassifyAndPrefixand account-merge logic FlatKV uses. This is useful for proving FlatKV state matches the current migration mapping and for debugging the translator.
--height; memIAVL does not replay WAL and instead opens snapshot-<height>/evm, or current/evm when --height is 0.
Usage
Flags
To locate a single diverging row between two runs, XOR the two differing 32-byte
bucket_digest hex values and pass the result to --find-hash; every matching entry is printed with its bucket, physical key, and values.