Skip to content

db migrate-changesets: preflight misclassifies every stock datadir as pruned (genesis alloc reverts are at block 0) #391

Description

@nekomoto911

Summary

gravity_node db migrate-changesets refuses to run on any stock datadir: the pruned-database preflight requires the earliest AccountChangeSets entry to be at block 1, but genesis initialization writes the genesis-alloc reverts at block 0 on every chain. The block-0 genesis entries are misclassified as evidence of pruning and the command exits 1 in under a second:

Error: changeset history is pruned; migrating a pruned database is not yet supported

As it stands, no existing (non-pruned, fully archival) datadir can be migrated to the static-file changeset layout.

Root cause

Two pieces, both verified against source and live data:

  1. Preflight checkcrates/cli/commands/src/db/migrate_changesets.rs:76-85 (introduced in 85521fc276ec3ae4db518a582be6fbb542521055 feat(cli): add reth db migrate-changesets command; unchanged by the crash-window fix 162bc04673201d18e66bc21ef832e3814bd6d639):

    // Pruned nodes whose earliest changeset is above block 1 are not supported: ...
    eyre::ensure!(
        provider
            .tx_ref()
            .cursor_read::<tables::AccountChangeSets>()?
            .first()?
            .is_none_or(|(block, _)| block == 1),
        "changeset history is pruned; migrating a pruned database is not yet supported"
    );

    The comment says "earliest changeset is above block 1", but the code rejects anything != 1 — so block 0 is caught by accident.

  2. Genesis writes changesets at block 0insert_genesis_state (crates/storage/db-common/src/init.rs:263-281) inserts a revert entry for every alloc account keyed by the genesis block number:

    reverts_init.insert(*address, (Some(None), ...));
    ...
    let all_reverts_init: RevertsInit = HashMap::from_iter([(block, reverts_init)]); // block = 0

    So every stock chain has AccountChangeSets/StorageChangeSets entries at block 0 by construction, and the preflight can never pass.

Reproduction (deterministic, <1 s, any stock datadir)

Stop a node, then:

gravity_node db --datadir <node>/data/reth \
  --chain <base>/genesis.json \
  --datadir.static-files <node>/data/reth \
  migrate-changesets

→ exit 1, changeset history is pruned; migrating a pruned database is not yet supported.

Evidence

Hit on the first-ever live execution of the command in our storage-v2 e2e (storage_v2_upgrade case in gravity-sdk), against a datadir produced by the full acceptance path: v1.7.5 chain with real history → rolling upgrade to gravity-reth-merge-v2.3.0 → Alpha forward activation → migrate. Probed on two independent nodes:

  • db get mdbx AccountChangeSets 0 <alloc-address> → entry present (info: null, i.e. the genesis-alloc revert shape);
  • blocks 1 / 2 / 5 / 100 entries all present — the history is contiguous from genesis;
  • no prune segments configured anywhere (reth.toml clean);
  • pre-migration table counts on the probed node: AccountChangeSets 15334 / StorageChangeSets 27896.

By construction from init.rs this should equally affect a fresh v2.3.0 datadir (not separately exercised).

Likely why crate-level tests didn't catch it: test fixtures that seed changesets from block 1 without going through init_genesis never produce the block-0 entries.

Suggested fix direction

  • Preflight: accept earliest block ∈ {0, 1} (or explicitly detect "gap above genesis" instead of != 1).
  • Decide how block 0 is represented in the static-file segments: the SF-fresh init path already anchors an empty block 0 (init.rs:182 comment "no changesets, so an empty block 0 anchors the append chain"), whereas legacy datadirs carry real revert entries at block 0 — the migration writer needs a defined answer (fold genesis reverts into the block-0 segment entry vs. treat block 0 as the empty anchor and start data at 1). That's a product decision; either way the preflight message should stop calling healthy databases pruned.

Environment

  • Branch gravity-reth-merge-v2.3.0, HEAD 8d4fa2a915e65bc19f6b022172f371d1367ee278 (built via gravity-sdk local [patch] integration, quick-release profile)
  • RocksDB backend, Debian 12 host

Related minor tooling observation (can split into its own issue)

While diagnosing: db list <dupsort-table> --json/--raw panics decoding composite RocksDB keys (db_tool/mod.rs:67 area), and bare db list requires a TTY. db list <table> --count is unaffected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions