Skip to content

feat: read-state bootstrap for co-located ephemeral/Regtest followers#10787

Draft
nuttycom wants to merge 9 commits into
ZcashFoundation:mainfrom
nuttycom:feat/read_state_regtest
Draft

feat: read-state bootstrap for co-located ephemeral/Regtest followers#10787
nuttycom wants to merge 9 commits into
ZcashFoundation:mainfrom
nuttycom:feat/read_state_regtest

Conversation

@nuttycom

@nuttycom nuttycom commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Draft for discussion, not a merge request. Posted so the team can see the concrete
Zebra-side changes while evaluating the approach in #10786.

Builds atop #10764

Motivation

A co-located read-state follower (e.g. Zallet's zebra-state backend) opens a node's
state DB as a read-only RocksDB secondary and follows its non-finalized best chain over the
indexer gRPC stream. It cannot follow an ephemeral or Regtest node today: an
ephemeral DB lives at a random temp path the follower can't discover, and a follower has no
way to learn a Regtest node's configured activation heights.

Solution

  • getreadstateinfo JSON-RPC method (zebra-only): reports the node's live state DB
    path, indexer gRPC address, DB format version/kind, and network identity (kind + genesis
    hash +, for Regtest, activation heights).
  • ReadRequest::StateDbInfoReadResponse::StateDbInfo: surfaces the live
    finalized-state DB path/version/kind from the read-state service (correct for ephemeral
    DBs, whose path can't be recomputed from config).
  • Explicit-path read-only open (Config::read_only_db_path): open a read-only secondary
    at an explicit on-disk path so a follower can tail an ephemeral primary's randomly-located
    DB.
  • (Foundational, already on the branch) FindForkPoint read request/helper for reorg
    tracking; don't flush read-only secondary DBs on shutdown.

Testing

  • New unit tests: ReadRequest::StateDbInfo reports the live DB; read-only open honors an
    explicit path; getreadstateinfo reports state + Regtest network info.
  • cargo fmt --all -- --check, cargo clippy -p zebra-state -p zebra-rpc -p zebrad --all-targets -- -D warnings, and cargo test -p zebra-state -p zebra-rpc pass locally
    (141 + 2 tests).

Notes for reviewers / known limitations

  • Custom (non-default) Testnets are not supported for bootstrap. Only Mainnet, the
    default Testnet, and Regtest report enough to reconstruct the network; a custom Testnet
    would be mis-reconstructed as the default public Testnet. Intentional for this scope.
  • Exposure: getreadstateinfo returns a local filesystem path and the indexer address.
    It is gated by the same JSON-RPC cookie auth as every method, is read-only, and only
    reflects config the operator already set. The reported indexer_grpc_addr may be a public
    bind address.

AI disclosure

Implemented with Claude Code assistance (design, code, and tests). The contributor is the
sole responsible author.

Closes #10786.

nuttycom and others added 9 commits June 23, 2026 13:38
A read-only secondary RocksDB instance has nothing to flush, and RocksDB
rejects `flush()`/`flush_wal()` on secondaries with "Not supported operation
in secondary mode". `DiskDb::shutdown` logged those benign failures at INFO
as "unexpected error flushing database ... during shutdown".

Model the database open mode as a `DbMode` enum (`Persistent`, `Ephemeral`,
`ReadOnlySecondary`) instead of separate `ephemeral`/`read_only` flags, making
the nonsensical "ephemeral read-only secondary" state -- which would delete the
primary's files on drop -- unrepresentable internally. `DiskDb::new` rejects that
flag combination with a `StateInitError::ReadOnlyEphemeralConflict` (a real error,
enforced in release builds, not a debug assertion), and shutdown skips flushing a
secondary. The existing file-deletion behaviour is preserved for every valid mode.

While here, match the expected RocksDB shutdown error by
`ErrorKind::ShutdownInProgress` rather than by substring, consistent with the
existing `e.kind()` check at the database open site.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Locates the most recent block in a locator that is on the best chain, reusing
find_chain_intersection. Best-chain-only; the locator carries side-chain info.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes find_fork_point as a ReadRequest/ReadResponse pair, dispatched against
the best chain, mirroring the FindBlockHashes family.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bootstrap a read-only state follower from a co-located node over JSON-RPC (enables ephemeral/Regtest)

2 participants