Repository navigation
refactor(storage): cache ChainConfig in Store - #563
Conversation
`Store::config()` hit the backend on every call and handed back a `Result`
that no caller could act on: every one of the fourteen call sites answered it
with `.expect("config exists")`. The config is written once in `init_store`
and has no setter, so the read could never fail for a live `Store` and the
round trip bought nothing.
Keep a plain copy on the `Store` and make the getter infallible. A copy rather
than an `Arc` because the value cannot go stale and is a single `u64` today.
It stays in `Metadata` under `KEY_CONFIG`: `from_db_state` reads it back to
reject a DB whose `genesis_time` disagrees with the config file, so the
persisted copy is load-bearing and only the per-call read goes away.
🤖 Kimi Code ReviewThis is a clean refactoring that eliminates unnecessary error handling and I/O for an immutable, bootstrap-time value. Correctness & Safety
Performance
Rust Idioms
Minor Note
Verdict: LGTM. No blockers. Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt |
🤖 Claude Code ReviewReview:
|
🤖 Codex Code ReviewNo correctness, security, or consensus-critical findings in this diff. The change is narrowly scoped and looks sound: Minor test gap: crates/storage/src/store.rs:2965 only asserts that I could not run the Rust tests here: direct Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
Greptile SummaryThe PR caches the immutable chain configuration directly in
Confidence Score: 5/5The PR appears safe to merge, with configuration caching consistent across bootstrap, database restoration, cloning, and test-driver replacement. Every production Store constructor derives the cached value from the same configuration governing persisted state, and no live mutation path can make the cache stale.
|
| Filename | Overview |
|---|---|
| crates/storage/src/store.rs | Adds the cached configuration field to every Store construction path while preserving persisted configuration validation during database restoration. |
| crates/blockchain/src/lib.rs | Updates consensus timing paths to use the infallible cached configuration without changing their calculations. |
| crates/blockchain/src/store.rs | Updates tick and proposal-head timing calculations for the new configuration accessor. |
| crates/blockchain/src/spec_test_runner.rs | Adapts fork-choice fixture timing to the infallible configuration accessor. |
| crates/net/rpc/src/genesis.rs | Reads the cached genesis time while preserving the existing RPC response contract. |
| crates/net/rpc/src/node.rs | Uses the cached genesis time for sync-distance calculation with existing overflow handling intact. |
| crates/net/rpc/tests/test_driver_e2e.rs | Updates test-driver coverage to assert against the new accessor. |
Reviews (1): Last reviewed commit: "refactor(storage): cache ChainConfig in ..." | Re-trigger Greptile
beacon-APIs lambdaclass#563 deprecates the v1 endpoint in favour of v2, which differs only in `dependent_root`: the block before the previous epoch rather than the block before the epoch. That is the block fulu's proposer lookahead actually depends on, since an epoch's proposers are written by the epoch transition into the epoch before it. v1's later root changes on reorgs that leave the duties untouched, so a validator client watching it refetches for nothing. Both versions share one handler body, parameterised by the dependent slot. Attester duties already used v2's formula and now share its helper.
Motivation
Store::config()opened a read view and deserializedMetadata["config"]on every call, then returned aResultno caller could act on. All fourteen call sites answered it the same way:The config is written once in
init_storeand has no setter, so for a liveStorethe read cannot fail and the round trip buys nothing. Several of those call sites sit on the tick path (on_tick,propose_block, block import), so they pay it every slot.Changes
Storegains aconfig: ChainConfigfield, populated ininit_storefrom the anchor state and infrom_db_statefrom the config it already parses for the genesis-time check.Arc: the value cannot go stale and is a singleu64today, so sharing it would only add indirection.config()becomes infallible and returns&ChainConfig..expect("config exists")from all call sites, which is why the diff reachesblockchain,rpc, and the spec-test runner rather than juststorage.KEY_CONFIGis still written and still read back byfrom_db_state, which refuses to resume a DB whose persistedgenesis_timedisagrees with the config file. Only the per-call read goes away, not the persistence.Testing
make fmt,make lintclean.cargo test --workspace --profile release-fast: 555 passed, 0 failed, 7 ignored (the usual#[ignore]crypto tests).Note
CLAUDE.mdgets a line aboutstore.config()being an infallible field read once this and #562 have both landed. Left out here to avoid a conflict between the two branches.