> ## Documentation Index
> Fetch the complete documentation index at: https://seilabs-docs-remove-retired-sei-rpcs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Giga SS Store Migration Guide

> Migrate a Sei RPC node to Giga SS Store: split EVM state into a dedicated state-store backend so non-EVM modules stop paying EVM write amplification.

Giga SS Store is the next step in Sei's storage evolution on top of [SeiDB](/node/node-operators#architecture).
It splits the hot EVM state into its own dedicated state-store (SS) database
so the node can scale toward the **\~150k TPS** target throughput, and so
non-EVM modules stop paying write amplification for EVM state.

After migration the SS layer is repartitioned into two cooperating stores:

| Layer                                    | Cosmos backend                | EVM backend                                                        |
| ---------------------------------------- | ----------------------------- | ------------------------------------------------------------------ |
| **SC** (State Commit, app hash)          | `memiavl`                     | FlatKV                                                             |
| **SS** (State Store, historical queries) | single PebbleDB MVCC database | dedicated EVM SS MVCC databases in the configured EVM SS directory |

Only the **SS** layer changes for this migration. SC layer config is untouched
and `memiavl` remains the authoritative source for the app hash, so this is
invisible to the network.

<Info>This guide tracks the canonical procedure in [`docs/migration/giga_store_migration.md`](https://github.com/sei-protocol/sei-chain/blob/main/docs/migration/giga_store_migration.md) inside `sei-chain`. Open an issue there if anything here drifts.</Info>

## Prerequisites

<Warning>This migration is supported on **RPC nodes only**. Validator nodes and archive nodes are not supported by this flow yet — do not run it against either.</Warning>

* A `seid` build with the `evm-ss-split` flag wired in (Sei v6.5 or later). Older
  releases used per-key `evm-ss-write-mode` / `evm-ss-read-mode` toggles; if your
  `app.toml` still has those keys, upgrade `seid` before continuing.
* `sc-enable = true` and `ss-enable = true` in `app.toml`. Both must stay enabled.
* A trusted RPC endpoint to state-sync from (chain ID and trust-height source).
* Disk headroom for two SS databases. The EVM split does not duplicate data, but
  during migration both the old and the new layouts may briefly coexist on disk.

The migration **requires a full state sync**. There is no in-place migration
path and no live "dual-write then split" workflow — the state sync wipes the
local data directory and imports a fresh snapshot into the new layout.

## Benefits

* EVM reads are served exclusively from a dedicated EVM SS database.
* Non-EVM modules no longer pay write amplification for EVM state.

## What's different about EVM SS

EVM SS is **point-query only by design** (`Get` / `Has`). Iteration is
explicitly disabled on the EVM backend for performance: the hot EVM read path
is tuned for direct key lookups, and cross-bucket scans would defeat the
per-type sub-DB layout. Any EVM read that needs iteration must stay on the
Cosmos SS side.

## Migration Steps

### Step 1: Update `app.toml`

Apply the following settings in `~/.sei/config/app.toml`:

```toml copy theme={"dark"}
[state-commit]
# State commit is untouched by this migration.
sc-enable = true

[state-store]
ss-enable = true

# Use PebbleDB for the Cosmos SS MVCC DB and every EVM SS sub-DB.
ss-backend = "pebbledb"

# Route EVM state to the dedicated EVM SS backend.
# When false (default), EVM state lives in the Cosmos SS backend alongside
# everything else. When true, EVM data is routed exclusively to the EVM SS
# backend; non-EVM data stays in Cosmos SS. No fallback between backends.
evm-ss-split = true
```

<Warning>Keep `ss-backend = "pebbledb"` during this migration. RocksDB support for the state store will be removed. No target release has been published. If the node already uses RocksDB, follow [Move off RocksDB](/node/node-operators#move-off-rocksdb).</Warning>

### Step 2: State sync into the new layout

Giga SS Store is fully compatible with the existing state-snapshot format. On
import, the composite state store routes each snapshot node based on the
importing node's `evm-ss-split`:

* With `evm-ss-split = true`, EVM snapshot nodes go only into EVM SS and
  non-EVM nodes go only into Cosmos SS.
* The import path normalizes legacy `evm_flatkv` snapshot nodes to `evm`, so
  snapshots produced by either the old or new FlatKV module are accepted.

Both stores end up fully populated at the snapshot height, so the node can
start serving reads immediately.

The full state-sync flow is documented in the
[Statesync guide](/node/statesync). The minimal shape for this migration:

```bash copy theme={"dark"}
export TRUST_HEIGHT_DELTA=10000
export MONIKER="<moniker>"
export CHAIN_ID="<chain_id>"
export PRIMARY_ENDPOINT="<rpc_endpoint>"
export SEID_HOME="$HOME/.sei"

# 1. Stop seid
sudo systemctl stop seid

# 2. Back up files you need to preserve and wipe local state
cp $SEID_HOME/data/priv_validator_state.json /tmp/priv_validator_state.json
cp $SEID_HOME/config/priv_validator_key.json   /tmp/priv_validator_key.json
cp $SEID_HOME/config/genesis.json              /tmp/genesis.json
rm -rf $SEID_HOME/data/*
rm -rf $SEID_HOME/wasm
rm -rf $SEID_HOME/config/priv_validator_key.json
rm -rf $SEID_HOME/config/genesis.json
rm -rf $SEID_HOME/config/config.toml

# 3. Re-init, re-apply config.toml and app.toml (set Step 1 values again)
seid init --chain-id "$CHAIN_ID" "$MONIKER"

# 4. Resolve trust height/hash and persistent peers against PRIMARY_ENDPOINT,
#    then update config.toml. See /node/statesync for the full snippet.

# 5. Restore the backed-up files
cp /tmp/priv_validator_state.json $SEID_HOME/data/priv_validator_state.json
cp /tmp/priv_validator_key.json   $SEID_HOME/config/priv_validator_key.json
cp /tmp/genesis.json              $SEID_HOME/config/genesis.json

# 6. Start seid
sudo systemctl restart seid
```

<Warning>Make sure `priv_validator_key.json` is in safe storage before deleting it from the config directory. Loss of this key is unrecoverable for a validator and is not relevant to RPC-only nodes — but if you're following this from the wrong checklist you'll find out the hard way.</Warning>

### Step 3: Verify the new layout

Once the state sync completes and the node starts producing blocks, confirm
Giga SS Store is active in two places.

**Startup logs.** All three lines should appear:

```text theme={"dark"}
"SeiDB SS is enabled"                       # with the configured `backend`
"SeiDB EVM StateStore optimization is enabled"  # with the `separateDBs` label
"EVM state store enabled"                   # with `dir` and `separateDBs` labels
```

**EVM RPC.** `debug_traceBlockByNumber` is the cleanest end-to-end check —
it forces the node to read EVM state out of the new EVM SS backend:

```bash copy theme={"dark"}
curl -s -X POST http://127.0.0.1:8545 \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"debug_traceBlockByNumber","params":["latest",{}],"id":1}'
```

The response should contain a `"result"` field rather than an RPC error.

## Safety checks

`seid` runs three DB-state checks at startup and refuses to launch if the EVM
SS and Cosmos SS DBs are inconsistent. They specifically catch the footgun of
flipping `evm-ss-split` from `false` to `true` without state syncing.

1. **EVM SS directory missing or empty** (before the EVM SS is opened). When
   `evm-ss-split = true`, the composite state store refuses to proceed if
   Cosmos SS already has committed history but the configured EVM SS directory
   does not exist or is empty. Failing before the sub-DBs are opened means a
   rejected config does not leave a confusing empty directory behind.
2. **EVM SS DB empty post-open, pre-recovery.** Belt-and-suspenders for (1)
   when the directory exists but its DBs are empty. The WAL only covers the
   last `KeepRecent` blocks, so replay cannot rebuild a fresh EVM SS from
   scratch.
3. **Mismatched earliest versions, post-recovery.** If the two DBs were
   populated from different snapshots (or pruned independently), historical
   reads would be inconsistent. A non-zero earliest-version divergence
   aborts startup.

If any check fires, the correct fix is either (a) complete the state sync
described above, or (b) set `evm-ss-split = false` and restart. If
the configured EVM SS directory is stale from a failed attempt, remove it
before state syncing.

## Rollback

To roll back:

1. Set `evm-ss-split = false` in `app.toml`.
2. Restart the node. The EVM SS DB is no longer opened but stays on disk until
   you remove it.

To fully reclaim the disk used by EVM SS, stop the node and delete
the configured EVM SS directory after reverting the setting.

<Warning>Cleanly rolling back to `evm-ss-split = false` requires another state sync. Under `evm-ss-split = true`, EVM writes go only to the EVM SS DB, so Cosmos SS will not have those writes. Restarting with `evm-ss-split = false` stops opening the EVM SS DB, but EVM-state queries will miss anything written after the Giga state sync until you re-state-sync without the split.</Warning>

## FlatKV EVM SC migration flow

Everything above concerns the **SS** (State Store) layer. The **SC** (State
Commit) layer has its own, separate migration path that moves the hot `evm/`
data out of `memiavl` and into FlatKV in place, without a state sync. It is
driven entirely by `app.toml`'s `sc-write-mode` and is coordinated across a
quorum by stopping the nodes, editing config, and restarting.

Unlike the SS split, the SC-side migration **does change how `evm/` data
contributes to the app hash** (memiavl IAVL root before the migration; FlatKV
lattice hash after). Because of that, every validator in a quorum must flip at
the same coordinated stop — a node flipped while its peers are still on the old
mode will produce a different AppHash on the very next block and consensus will
halt. The safe sequence is always: stop everyone, rewrite `app.toml`
everywhere, restart everyone.

<Warning>This SC-side FlatKV EVM migration flow is exercised by the cluster/devnet integration harness. Do not run it against testnet/mainnet nodes unless the release notes for your version explicitly call it out as supported.</Warning>

### Write modes

The migration is a transition from the `memiavl_only` write mode (v0, where
memiavl is the sole SC backend and FlatKV is not allocated) to `migrate_evm`
(the in-flight mode that drains `evm/` keys from memiavl into FlatKV). Once the
migration completes, operators flip `sc-write-mode` again to `evm_migrated` so
subsequent restarts don't spin up the migration manager.

`evm_migrated` is not the last stop. Three further modes sit between it and
the terminal mode: `migrate_all_but_bank` (drains every remaining module
except `bank/` from memiavl into FlatKV), `all_migrated_but_bank` (the steady
state once that drain completes), and `migrate_bank` (drains the final
`bank/` module).

`flatkv_only` is the fully-supported **terminal** steady-state write mode:
FlatKV is the sole SC backend and `memiavl` is not allocated at all. In this
mode every module's SC state is served from FlatKV, and state-sync snapshot
export/restore plus app-hash parity work correctly, so a node can boot directly
into the post-migration shape without ever running the migration manager.
`flatkv_only` is only valid for a node whose modules have all been drained out
of memiavl — it is not a flip target for an `evm_migrated` node, which still
holds `bank/` and every other non-EVM module in memiavl. It becomes the valid
flip target once `migrate_bank` reports complete on the node (migration
version 3, all modules in FlatKV): restart with `sc-write-mode = "flatkv_only"`
to reach the terminal steady state. It is also the mode for a node that boots
or state-syncs directly into the post-migration shape without ever running the
migration manager.

<Note>A correctness bug in the WAL replay path — where empty (zero-length)
values written with no delete flag were dropped on replay (catchup, read-only
clone, snapshot export, and state-sync restore), diverging the FlatKV state and
the consensus AppHash from the live chain — is fixed. Empty-value writes are now
preserved across a WAL round-trip and state-sync, which is what makes
`flatkv_only` state-sync reliable.</Note>

While in a migration mode:

* Caller reads of not-yet-migrated keys fall back to FlatKV for brand-new keys
  written after the migration started, and to memiavl otherwise.
* Iteration is served by a merging iterator over both backends (memiavl
  queried first, FlatKV winning on ties), so range scans see the complete key
  set during a migration.
* The migration boundary advances at most once per block.

### Operator-facing knobs

**`sc-keys-to-migrate-per-block`** (`app.toml`, `[state-commit]` section)
controls how many EVM keys the in-flight migration drains from memiavl into
FlatKV per block. It defaults to `1024`, which is appropriate for production
drains. Lowering it spreads the migration across more blocks. It must be `> 0`,
and it is ignored entirely when `sc-write-mode` is not a migration mode.

```toml copy theme={"dark"}
[state-commit]
sc-write-mode = "migrate_evm"
sc-keys-to-migrate-per-block = 1024
```

**`GIGA_MIGRATE_FROM_MEMIAVL`** is a cluster/docker environment variable used by
the local devnet setup. When set to `true` it boots every node in
`memiavl_only` mode — the v0 starting point for the FlatKV EVM migrate flow. It
is mutually exclusive with `GIGA_STORAGE`; if both are set,
`GIGA_MIGRATE_FROM_MEMIAVL` takes precedence.

```bash copy theme={"dark"}
GIGA_MIGRATE_FROM_MEMIAVL=true make docker-cluster-start
```

### Checking migration status

The `seidb migrate-evm-status` subcommand reports the on-disk FlatKV EVM
migrate state of a FlatKV directory as JSON. It clones the latest snapshot and
WAL into a temp dir before reading, so it can be run against a live node's data
directory without contending for the FlatKV writer lock.

```bash copy theme={"dark"}
seidb migrate-evm-status --db-dir <flatkv-dir> [--height <n>]
# Full flag and JSON-field reference: /node/technical-reference (seidb Tooling Commands)
```

`--db-dir` (short `-d`) points at the FlatKV data directory; `--height` selects
a target version (`0`, the default, selects the latest available version). The
emitted JSON includes `migrate_evm_complete` (true once the migration finishes),
`migration_version`, `version_at`, and whether an in-flight boundary is still
present. Poll it until `migrate_evm_complete` reports `true` on every validator
before flipping `sc-write-mode` to `evm_migrated`.

When the migration finishes, each node also emits a `migration complete` summary
log line and a set of `seidb_migration_*` OpenTelemetry counters covering keys
and bytes migrated.

### Importing EVM State from memIAVL into FlatKV

The `seidb import-flatkv-from-memiavl` command populates a FlatKV store from an existing memIAVL tree, for nodes moving to the FlatKV EVM commit store without a state sync. Two safety properties matter:

* The import height must equal the memIAVL latest version. The command refuses to import at a lower height (the composite store's version reconciliation would silently roll memIAVL back and truncate blocks) and refuses a higher one. Check the current version first with `seidb memiavl-latest-version`, and roll memIAVL back to the target height before importing if needed.
* Overwriting existing committed FlatKV data requires the explicit `--force` flag.

## FAQ

### Where do the data files live after migrating?

* Cosmos SS data uses `data/pebbledb/` in the legacy layout and
  `data/state_store/cosmos/pebbledb/` in the current layout.
* EVM SS data uses `data/evm_ss/` in the legacy layout and
  `data/state_store/evm/pebbledb/` in the current layout.
* Nodes created before the layout change keep their legacy paths automatically
  (a legacy directory takes precedence when present); new nodes use the
  current layout.
* A non-empty `ss-db-directory` or `evm-ss-db-directory` overrides the
  corresponding default path.
* SC data (`memiavl` + FlatKV) is untouched by this migration.

### Does Giga SS Store change the app hash or consensus?

No. The SC layer is unchanged, so `memiavl` remains the authoritative source
for the app hash. Giga SS Store is a per-node SS change that is invisible to
the network.

### Can I migrate a validator node with this guide?

Not yet. This migration guide is for RPC nodes only.

### Can I migrate an archive node with this guide?

Not yet. Archive-node migration is out of scope for this guide.

### Can I toggle back to `evm-ss-split = false` after enabling it?

Yes, but cleanly rolling back requires another state sync — see the
[Rollback](#rollback) section above.

### Why can't I just flip `evm-ss-split = true` on a running node?

Because `evm-ss-split = true` requires the EVM SS DB to already contain the
full history that Cosmos SS has. A live flip would leave the EVM SS DB empty
while the composite store refuses to fall back to Cosmos SS, which would
translate into missing EVM state at query time. The safety checks above
block this scenario at startup.

### Does Giga SS Store support historical proofs?

No, same as SeiDB. SS stores raw KVs and does not reconstruct IAVL-style
proofs.

### Does enabling Giga Storage change the receipt backend?

In the `localnode` and `rpcnode` configuration scripts, setting
`GIGA_STORAGE=true` defaults `RECEIPT_BACKEND` to `pebble` unless you set
`RECEIPT_BACKEND` explicitly. To use a different value while running with Giga
Storage, provide an explicit `RECEIPT_BACKEND` env var, which takes precedence
over the default.

`pebbledb` (aka `pebble`) is now the only supported receipt-store backend. The
former `parquet` option has been removed: setting `RECEIPT_BACKEND=parquet` (or
`rs-backend = "parquet"` in `app.toml`) is rejected with an error
(`unsupported receipt-store backend "parquet"; supported: pebbledb`).
