Skip to content

Conversation

@hanabi1224
Copy link
Contributor

@hanabi1224 hanabi1224 commented Sep 19, 2025

Summary of changes

Changes introduced in this pull request:

  • populate tipset_by_height cache on startup
  • refactor ChainStore and ChainIndex to make all fields private

Reference issue to close (if applicable)

Closes

Other information and links

Change checklist

  • I have performed a self-review of my own code,
  • I have made corresponding changes to the documentation. All new code adheres to the team's documentation standards,
  • I have added tests that prove my fix is effective or that my feature works (if possible),
  • I have made sure the CHANGELOG is up-to-date. All user-facing changes should be reflected in this document.

Summary by CodeRabbit

  • Performance

    • Faster startup by warming the tipset cache in the background.
    • Larger tipset cache for quicker historical lookups.
  • Reliability

    • More consistent access to chain data and configuration across components.
    • Validation and RPC paths now use the blockstore handle more consistently.
  • Tools

    • Offline server and CLI updated to the new initialization flow; behavior unchanged.
  • Refactor

    • Unified public accessors for chain data and configuration, reducing direct state exposure.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 19, 2025

Walkthrough

ChainStore and ChainIndex fields were made private with new accessor methods (including blockstore() returning &Arc). StateManager no longer holds ChainConfig and its constructors now take only Arc. Call sites across sync, RPC, interpreter, tools, daemon, and tests updated; daemon startup now pre-warms tipset cache.

Changes

Cohort / File(s) Summary
Core store API refactor
src/chain/store/chain_store.rs, src/chain/store/index.rs
Privatized ChainStore/ChainIndex fields; added accessors chain_index(), chain_config(), db(); blockstore() now returns &Arc<DB>; increased tipset_by_height cache capacity.
StateManager API & internals
src/state_manager/mod.rs, src/state_manager/tests.rs, src/state_manager/utils.rs
Removed chain_config field; constructors now take only Arc<ChainStore>; added chain_index()/chain_config()/blockstore() accessors and an owned blockstore helper; updated derives and internal call sites.
Daemon startup & metrics
src/daemon/context.rs, src/daemon/mod.rs
Added AppContext::chain_store(); construct StateManager with only ChainStore; added warmup_in_background to populate tipset cache; metrics/state tree now use blockstore().
Sync & validation flows
src/chain_sync/..., src/chain_sync/validation.rs
Replaced field accesses (chain_index, chain_config, db) with accessor calls (chain_index(), chain_config(), blockstore()) across sync and validation paths.
Interpreter & VM
src/interpreter/fvm2.rs, .../fvm3.rs, .../fvm4.rs, src/interpreter/vm.rs
Switched DB retrieval from field borrows to chain_index.db() and adjusted Arc::clone(...) usage; semantics unchanged.
RPC, filters & methods
src/rpc/methods/eth.rs, src/rpc/methods/eth/filter/mod.rs, src/rpc/mod.rs, src/rpc/methods/sync.rs
Replaced chain_index/chain_config field accessors with chain_index()/chain_config() and switched blockstore usage to blockstore()/ctx.store() where applicable; updated StateManager constructor calls in tests.
Networking & msg pool
src/libp2p/chain_exchange/provider.rs, src/message_pool/msgpool/provider.rs
Replaced chain_index field access with chain_index() for tipset loading and chain construction.
FIL-CNS validation
src/fil_cns/validation.rs
Updated lookups to call chain_index() rather than access the field directly.
Tools & servers
src/tool/... (offline_server/server.rs, subcommands/*)
Move chain_config into ChainStore::new by value (no clone); updated StateManager::new calls to single-arg; adjusted call sites to new accessors.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Daemon as Daemon::start_services
  participant CS as ChainStore
  participant SM as StateManager
  participant Warm as warmup_in_background
  participant Metrics as Metrics/StateTree

  Daemon->>CS: ChainStore::new(..., chain_config)  %% chain_config moved into ChainStore
  Daemon->>SM: StateManager::new(Arc<ChainStore>)
  par Background warmup
    Daemon->>Warm: spawn blocking task
    Warm->>CS: chain_index().tipset_by_height(1, heaviest, TakeOlder)  %% prefill cache
    Warm-->>Daemon: log success/failure + duration
  and Metrics init
    Daemon->>Metrics: init metrics
    Metrics->>CS: blockstore() -> used for StateTree / collectors
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • LesnyRumcajs
  • akaladarshi
  • sudo-shashank

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly and accurately describes the primary user-facing change: populating the tipset_by_height cache on startup. The PR summary and diffs show a warmup_in_background task that populates tipset_by_height at startup, so the title matches the main change. The ancillary API refactor (making ChainStore/ChainIndex fields private) is acceptable to omit from the title since it is not the primary feature.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hm/populate_tipset_by_height_cache

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 019b673 and 42dfb6f.

📒 Files selected for processing (1)
  • src/daemon/mod.rs (6 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: hanabi1224
PR: ChainSafe/forest#5944
File: src/chain/store/index.rs:0-0
Timestamp: 2025-08-18T03:09:47.932Z
Learning: In Forest's tipset_by_height caching implementation, hanabi1224 prefers performance-conscious solutions that leverage finality guarantees rather than expensive chain walking for fork detection. The approach of constraining cache lookups to finalized epochs (using CHECKPOINT_INTERVAL >= CHAIN_FINALITY) provides fork safety without the performance cost of ancestry verification.
Learnt from: hanabi1224
PR: ChainSafe/forest#5930
File: build.rs:64-77
Timestamp: 2025-08-13T09:43:20.301Z
Learning: hanabi1224 prefers hard compile-time errors in build scripts rather than runtime safeguards or collision detection, believing it's better to fail fast and fix root causes of issues like malformed snapshot names.
📚 Learning: 2025-08-18T03:09:47.932Z
Learnt from: hanabi1224
PR: ChainSafe/forest#5944
File: src/chain/store/index.rs:0-0
Timestamp: 2025-08-18T03:09:47.932Z
Learning: In Forest's tipset_by_height caching implementation, hanabi1224 prefers performance-conscious solutions that leverage finality guarantees rather than expensive chain walking for fork detection. The approach of constraining cache lookups to finalized epochs (using CHECKPOINT_INTERVAL >= CHAIN_FINALITY) provides fork safety without the performance cost of ancestry verification.

Applied to files:

  • src/daemon/mod.rs
📚 Learning: 2025-09-17T11:32:44.185Z
Learnt from: hanabi1224
PR: ChainSafe/forest#6074
File: src/rpc/methods/chain.rs:55-56
Timestamp: 2025-09-17T11:32:44.185Z
Learning: In the Forest codebase, hanabi1224 prefers that CodeRabbit should not warn about potential compilation issues (such as Send bounds, async/await compatibility, etc.) since they are already guarded by CI tests. Focus should be on higher-level design and logic issues instead.

Applied to files:

  • src/daemon/mod.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: tests-release
  • GitHub Check: tests
  • GitHub Check: Build Ubuntu
  • GitHub Check: Build MacOS
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: All lint checks
🔇 Additional comments (4)
src/daemon/mod.rs (4)

11-11: Import looks correct.

ResolveNullTipset is used in the warmup; OK.


42-42: OK to add Instant.

Used for warmup timing; fine.


226-229: Good refactor to blockstore.

StateTree::new_from_root(cs.blockstore(), …) aligns with the blockstore‑centric API; closure uses Weak to avoid GC deadlock. LGTM.


520-523: Indexer: pass blockstore — good.

EthMappingCollector::new(chain_store.blockstore(), …) is consistent with the refactor.


Comment @coderabbitai help to get the list of available commands and usage tips.

@hanabi1224 hanabi1224 marked this pull request as ready for review September 19, 2025 11:30
@hanabi1224 hanabi1224 requested a review from a team as a code owner September 19, 2025 11:30
@hanabi1224 hanabi1224 requested review from LesnyRumcajs and akaladarshi and removed request for a team September 19, 2025 11:30
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/state_manager/mod.rs (1)

212-226: Bug: populate_cache stores the wrong state_root for the parent tipset.

For a (child, parent) window, the state/receipt for the parent are child.parent_state() and child.min_ticket_block().message_receipts. Using child.min_ticket_block().state_root caches the child’s post-state under the parent key.

Apply this fix:

-            self.cache.insert(
-                parent.key().clone(),
-                StateOutputValue {
-                    state_root: child.min_ticket_block().state_root,
-                    receipt_root: child.min_ticket_block().message_receipts,
-                },
-            )
+            self.cache.insert(
+                parent.key().clone(),
+                StateOutputValue {
+                    // Parent’s state/receipts are recorded by the child tipset.
+                    state_root: *child.parent_state(),
+                    receipt_root: child.min_ticket_block().message_receipts,
+                },
+            )
🧹 Nitpick comments (2)
src/chain_sync/tipset_syncer.rs (1)

203-203: Avoid cloning Arc in hot paths — accept a reference instead

get_lookback_tipset_for_round is called from: src/fil_cns/validation.rs, src/chain_sync/tipset_syncer.rs, src/state_manager/mod.rs, src/interpreter/fvm2.rs, src/interpreter/fvm3.rs, src/interpreter/fvm4.rs, src/rpc/methods/miner.rs. Arc::clone() only increments the refcount, but if this is a hot path refactor the function to take &ChainIndex (or &Arc<ChainIndex<...>>) and update callers to eliminate repeated Arc::clone() overhead.

src/daemon/mod.rs (1)

226-229: Metrics closure uses new accessors correctly; consider caching.

This constructs a StateTree on every scrape. If this becomes hot, cache the actor major version keyed by head tipset key and update it on head changes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between caaba3d and 40a8502.

📒 Files selected for processing (26)
  • src/chain/store/chain_store.rs (4 hunks)
  • src/chain/store/index.rs (3 hunks)
  • src/chain_sync/chain_follower.rs (5 hunks)
  • src/chain_sync/tipset_syncer.rs (2 hunks)
  • src/chain_sync/validation.rs (1 hunks)
  • src/daemon/context.rs (2 hunks)
  • src/daemon/mod.rs (6 hunks)
  • src/fil_cns/validation.rs (3 hunks)
  • src/interpreter/fvm2.rs (1 hunks)
  • src/interpreter/fvm3.rs (1 hunks)
  • src/interpreter/fvm4.rs (1 hunks)
  • src/interpreter/vm.rs (3 hunks)
  • src/libp2p/chain_exchange/provider.rs (2 hunks)
  • src/message_pool/msgpool/provider.rs (1 hunks)
  • src/rpc/methods/eth.rs (4 hunks)
  • src/rpc/methods/eth/filter/mod.rs (1 hunks)
  • src/rpc/methods/sync.rs (2 hunks)
  • src/rpc/mod.rs (1 hunks)
  • src/state_manager/mod.rs (27 hunks)
  • src/state_manager/tests.rs (6 hunks)
  • src/state_manager/utils.rs (2 hunks)
  • src/tool/offline_server/server.rs (1 hunks)
  • src/tool/subcommands/api_cmd/api_compare_tests.rs (1 hunks)
  • src/tool/subcommands/api_cmd/generate_test_snapshot.rs (1 hunks)
  • src/tool/subcommands/api_cmd/test_snapshot.rs (1 hunks)
  • src/tool/subcommands/index_cmd.rs (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: hanabi1224
PR: ChainSafe/forest#5944
File: src/chain/store/index.rs:0-0
Timestamp: 2025-08-18T03:09:47.932Z
Learning: In Forest's tipset_by_height caching implementation, hanabi1224 prefers performance-conscious solutions that leverage finality guarantees rather than expensive chain walking for fork detection. The approach of constraining cache lookups to finalized epochs (using CHECKPOINT_INTERVAL >= CHAIN_FINALITY) provides fork safety without the performance cost of ancestry verification.
Learnt from: hanabi1224
PR: ChainSafe/forest#5930
File: build.rs:64-77
Timestamp: 2025-08-13T09:43:20.301Z
Learning: hanabi1224 prefers hard compile-time errors in build scripts rather than runtime safeguards or collision detection, believing it's better to fail fast and fix root causes of issues like malformed snapshot names.
📚 Learning: 2025-08-08T12:11:55.266Z
Learnt from: hanabi1224
PR: ChainSafe/forest#5867
File: src/ipld/util.rs:461-487
Timestamp: 2025-08-08T12:11:55.266Z
Learning: Forest (src/ipld/util.rs, Rust): In UnorderedChainStream::poll_next, dropping `extract_sender` (when no more tipsets and the extract queue is empty) is the intended shutdown signal for workers. Any subsequent attempt to enqueue work after this drop is a logic error and should be treated as an error; do not change `send()` to ignore a missing sender.

Applied to files:

  • src/message_pool/msgpool/provider.rs
🧬 Code graph analysis (18)
src/interpreter/vm.rs (3)
src/chain/store/chain_store.rs (1)
  • chain_index (247-249)
src/rpc/mod.rs (1)
  • chain_index (472-474)
src/state_manager/mod.rs (1)
  • chain_index (290-292)
src/rpc/methods/eth/filter/mod.rs (3)
src/rpc/methods/sync.rs (1)
  • ctx (177-252)
src/tool/subcommands/api_cmd/generate_test_snapshot.rs (1)
  • ctx (106-159)
src/tool/subcommands/api_cmd/test_snapshot.rs (1)
  • ctx (127-178)
src/state_manager/tests.rs (1)
src/state_manager/mod.rs (2)
  • new (180-182)
  • chain_store (285-287)
src/interpreter/fvm2.rs (5)
src/shim/state_tree.rs (3)
  • new_from_root (168-188)
  • new (156-166)
  • new (422-436)
src/chain/store/chain_store.rs (2)
  • new (116-142)
  • new (572-576)
src/interpreter/fvm3.rs (1)
  • new (48-65)
src/interpreter/fvm4.rs (1)
  • new (48-65)
src/interpreter/vm.rs (1)
  • new (175-271)
src/interpreter/fvm3.rs (2)
src/chain/store/chain_store.rs (2)
  • new (116-142)
  • new (572-576)
src/interpreter/vm.rs (1)
  • new (175-271)
src/tool/subcommands/api_cmd/generate_test_snapshot.rs (2)
src/chain/store/chain_store.rs (3)
  • chain_config (252-254)
  • new (116-142)
  • new (572-576)
src/state_manager/mod.rs (3)
  • chain_config (295-297)
  • new (180-182)
  • chain_store (285-287)
src/interpreter/fvm4.rs (2)
src/chain/store/chain_store.rs (2)
  • new (116-142)
  • new (572-576)
src/chain/store/index.rs (1)
  • new (42-46)
src/tool/offline_server/server.rs (4)
src/chain/store/chain_store.rs (3)
  • chain_config (252-254)
  • new (116-142)
  • new (572-576)
src/daemon/context.rs (2)
  • chain_config (65-67)
  • chain_store (69-71)
src/rpc/mod.rs (2)
  • chain_config (476-478)
  • chain_store (468-470)
src/state_manager/mod.rs (3)
  • chain_config (295-297)
  • new (180-182)
  • chain_store (285-287)
src/fil_cns/validation.rs (3)
src/daemon/context.rs (1)
  • chain_store (69-71)
src/rpc/mod.rs (1)
  • chain_store (468-470)
src/state_manager/mod.rs (1)
  • chain_store (285-287)
src/daemon/context.rs (2)
src/rpc/mod.rs (1)
  • chain_store (468-470)
src/state_manager/mod.rs (2)
  • chain_store (285-287)
  • new (180-182)
src/tool/subcommands/api_cmd/test_snapshot.rs (2)
src/chain/store/chain_store.rs (3)
  • chain_config (252-254)
  • new (116-142)
  • new (572-576)
src/state_manager/mod.rs (3)
  • chain_config (295-297)
  • new (180-182)
  • chain_store (285-287)
src/tool/subcommands/index_cmd.rs (2)
src/chain/store/chain_store.rs (3)
  • chain_config (252-254)
  • new (116-142)
  • new (572-576)
src/state_manager/mod.rs (3)
  • chain_config (295-297)
  • new (180-182)
  • chain_store (285-287)
src/rpc/methods/sync.rs (3)
src/rpc/mod.rs (2)
  • store (480-482)
  • chain_config (476-478)
src/chain/store/chain_store.rs (4)
  • block_messages (469-482)
  • chain_config (252-254)
  • new (116-142)
  • new (572-576)
src/state_manager/mod.rs (2)
  • chain_config (295-297)
  • new (180-182)
src/tool/subcommands/api_cmd/api_compare_tests.rs (2)
src/chain/store/chain_store.rs (1)
  • chain_config (252-254)
src/state_manager/mod.rs (3)
  • chain_config (295-297)
  • new (180-182)
  • chain_store (285-287)
src/chain_sync/chain_follower.rs (3)
src/daemon/context.rs (1)
  • chain_store (69-71)
src/rpc/mod.rs (1)
  • chain_store (468-470)
src/state_manager/mod.rs (1)
  • chain_store (285-287)
src/chain/store/chain_store.rs (4)
src/state_manager/mod.rs (3)
  • blockstore (276-278)
  • chain_index (290-292)
  • chain_config (295-297)
src/rpc/mod.rs (2)
  • chain_index (472-474)
  • chain_config (476-478)
src/daemon/context.rs (1)
  • chain_config (65-67)
src/tool/subcommands/archive_cmd.rs (1)
  • genesis_timestamp (1145-1149)
src/state_manager/mod.rs (5)
src/chain/store/chain_store.rs (6)
  • new (116-142)
  • new (572-576)
  • blockstore (242-244)
  • chain_index (247-249)
  • chain_config (252-254)
  • block_messages (469-482)
src/interpreter/vm.rs (1)
  • new (175-271)
src/networks/mod.rs (4)
  • epoch (468-481)
  • chain_config (737-742)
  • network_version (427-432)
  • network_version (745-755)
src/state_manager/chain_rand.rs (5)
  • clone (27-34)
  • state (191-191)
  • state (194-194)
  • state (209-209)
  • state (211-211)
src/state_migration/mod.rs (1)
  • run_state_migrations (93-162)
src/daemon/mod.rs (4)
src/chain/store/index.rs (1)
  • chain (191-198)
src/shim/state_tree.rs (1)
  • new_from_root (168-188)
src/daemon/context.rs (1)
  • chain_store (69-71)
src/state_manager/mod.rs (1)
  • chain_store (285-287)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: tests
  • GitHub Check: tests-release
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: Build Ubuntu
  • GitHub Check: Build MacOS
  • GitHub Check: All lint checks
🔇 Additional comments (48)
src/chain/store/index.rs (3)

29-29: Proper encapsulation of DB field

The change from pub db: DB to private db: DB correctly implements encapsulation, preventing direct field access and ensuring all DB access goes through the controlled accessor method.


48-50: Consistent accessor pattern

The new db() accessor method provides controlled access to the underlying database, maintaining consistency with the broader refactor pattern seen across the codebase.


132-133: Cache size increase with mainnet capacity rationale

The cache size increase from 4096 to 20480 entries is well-justified by the comment explaining mainnet capacity requirements (20480 * 900 = 18432000 epochs). This aligns with the PR's objective to populate the tipset_by_height cache on startup for better performance.

src/rpc/mod.rs (1)

473-473: Correct delegation to new accessor pattern

The change from direct field access (self.chain_store().chain_index) to method call (self.chain_store().chain_index()) properly utilizes the new accessor pattern while maintaining the same return type and functionality.

src/chain_sync/validation.rs (1)

76-76: Proper use of blockstore accessor

The change from &chainstore.db to chainstore.blockstore() correctly adapts to the new accessor-based API. The blockstore() method now returns &Arc<DB> as indicated in the summary, which provides the same functionality but through a controlled interface.

src/rpc/methods/eth/filter/mod.rs (1)

431-431: Consistent chain index accessor usage

The change from ctx.chain_store().chain_index.tipset_by_height(...) to ctx.chain_index().tipset_by_height(...) follows the established pattern in the broader refactor, using the accessor method instead of direct field access.

src/message_pool/msgpool/provider.rs (1)

118-118: Proper accessor method usage

The change from direct field access to chain_index() method call maintains the same functionality while adhering to the new encapsulation pattern established throughout the codebase.

src/state_manager/utils.rs (1)

48-48: Consistent config access through new accessor pattern

Both changes from self.chain_config.policy to self.chain_config().policy properly utilize the new accessor method. The functionality remains identical while following the established pattern of accessing configuration through methods rather than direct field access.

Also applies to: 60-60

src/tool/offline_server/server.rs (2)

97-97: Move semantics for chain_config in ChainStore::new

The change from chain_config.clone() to chain_config correctly transfers ownership of the Arc<ChainConfig> to the ChainStore::new method, which now consumes the value directly rather than requiring a clone.


100-100: Updated StateManager constructor signature

The change from StateManager::new(chain_store.clone(), chain_config) to StateManager::new(chain_store.clone()) correctly reflects the updated constructor signature where StateManager no longer takes a separate chain_config parameter but retrieves it from the ChainStore.

src/tool/subcommands/api_cmd/api_compare_tests.rs (1)

2166-2166: Updated initialization pattern for ChainStore and StateManager

Both changes correctly follow the new initialization pattern established across the codebase:

  1. Line 2166: chain_config is moved into ChainStore::new by value instead of being cloned
  2. Line 2169: StateManager::new now takes only the chain_store parameter, obtaining the config internally

This maintains the same functionality while adhering to the refactored API design.

Also applies to: 2169-2169

src/interpreter/fvm4.rs (3)

90-90: LGTM!

The change to use self.chain_index.db() accessor is correct and consistent with the refactoring to privatize ChainIndex fields.


96-96: LGTM!

The change to use self.chain_index.db() for TrackingBlockstore is consistent with the accessor pattern.


103-103: LGTM!

The change to use Arc::clone(self.chain_index.db()) follows the same accessor pattern consistently.

src/interpreter/vm.rs (3)

207-207: LGTM!

The change from direct field access to chain_index.db() accessor method is correct and aligns with the refactoring to make ChainIndex fields private.


235-235: LGTM!

Consistent use of the chain_index.db() accessor for V3 FVM initialization.


258-258: LGTM!

Consistent use of the chain_index.db() accessor for V2 FVM initialization.

src/tool/subcommands/api_cmd/generate_test_snapshot.rs (1)

119-131: LGTM!

The refactored initialization correctly moves the chain_config into ChainStore::new and updates StateManager::new to take only the chain store, aligning with the broader API changes to encapsulate configuration within ChainStore.

src/interpreter/fvm3.rs (3)

91-91: LGTM!

The change to use Arc::clone(self.chain_index.db()) is correct and consistent with the accessor pattern.


97-97: LGTM!

The change to use self.chain_index.db() for TrackingBlockstore is consistent with the accessor pattern.


103-103: LGTM!

The change to use Arc::clone(self.chain_index.db()) follows the same accessor pattern consistently.

src/chain_sync/tipset_syncer.rs (2)

192-193: LGTM!

The change from direct field access to the chain_index() accessor method is correct and consistent with the ChainStore refactoring.


203-204: LGTM!

The changes to use accessor methods chain_index() and chain_config() properly align with the privatized fields in ChainStore.

src/interpreter/fvm2.rs (3)

86-87: LGTM: Accessor method transition for database access.

The change from direct field access (&self.chain_index.db) to method call (self.chain_index.db()) aligns with the broader refactor to privatize ChainIndex fields and expose them through accessor methods.


93-93: LGTM: Consistent accessor method usage.

Properly updated to use the new db() accessor method instead of direct field access.


99-99: LGTM: Final accessor method update completes the transition.

All database access points in this method have been consistently updated to use the new accessor pattern.

src/daemon/context.rs (2)

69-71: LGTM: Clean delegation to StateManager's chain store accessor.

This new accessor method provides a clean way to access the chain store through the AppContext, maintaining proper encapsulation by delegating to the StateManager's chain_store() method.


254-254: LGTM: StateManager constructor signature simplified.

The StateManager constructor now takes only the ChainStore parameter, with ChainConfig being accessible through chain_store.chain_config(). This simplifies the constructor while maintaining functionality.

src/libp2p/chain_exchange/provider.rs (1)

34-34: LGTM: Consistent accessor method usage throughout.

Both instances of chain_store.chain_index have been properly updated to chain_store.chain_index() to use the new accessor method pattern. This maintains consistency with the broader refactoring effort.

Also applies to: 48-48

src/tool/subcommands/api_cmd/test_snapshot.rs (1)

145-145: LGTM: Constructor signature updates for consistency.

Both changes align with the broader refactor:

  1. ChainStore::new now takes chain_config directly as Arc
  2. StateManager::new simplified to take only the chain_store parameter

These changes maintain the same functionality while using the new constructor signatures.

Also applies to: 150-150

src/chain_sync/chain_follower.rs (3)

482-484: LGTM: Accessor method transition in tipset loading.

The change from chain_store.chain_index to chain_store.chain_index() properly uses the new accessor method while maintaining identical functionality for tipset retrieval.


624-624: LGTM: Configuration access through chain store.

Both instances now use self.cs.chain_config() to access the configuration through the chain store, maintaining consistency with the broader refactor pattern.

Also applies to: 636-636


961-961: LGTM: Test updates for blockstore accessor.

Both test methods now use cs.blockstore().clone() instead of direct field access, ensuring tests remain compatible with the new accessor pattern.

Also applies to: 1025-1025

src/rpc/methods/sync.rs (2)

124-124: LGTM: Streamlined blockstore access.

The change from &ctx.chain_store().db to ctx.store() simplifies the blockstore access pattern. The ctx.store() method delegates to chain_store().blockstore(), providing cleaner, more direct access to the underlying blockstore.


196-196: LGTM: Constructor signature updates in test setup.

Both changes align with the refactored APIs:

  1. ChainStore::new now takes chain_config directly (line 196)
  2. StateManager::new simplified to single parameter (line 202)

The test setup maintains identical functionality with the new constructor signatures.

Also applies to: 202-202

src/state_manager/tests.rs (1)

72-72: LGTM: Consistent StateManager constructor updates across all tests.

All six test instances have been properly updated to use the simplified StateManager::new constructor that takes only the chain_store parameter. The ChainConfig is now accessed through the chain store's accessor methods, maintaining the same functionality while following the new API design.

This demonstrates thorough test coverage updates that align with the broader refactoring effort.

Also applies to: 134-134, 167-167, 226-226, 269-269, 312-312

src/fil_cns/validation.rs (1)

60-62: Accessor refactor looks correct.

Switched to ChainStore::chain_index() and StateManager/ChainStore accessors without altering behavior. No issues spotted.

Also applies to: 75-79, 85-89

src/tool/subcommands/index_cmd.rs (1)

74-76: Constructor/API updates and accessor usage LGTM.

  • Moving chain_config into ChainStore::new and using StateManager::new(chain_store) aligns with the new API.
  • chain_index().tipset_by_height() call is correct for resolving "from".

Also applies to: 78-79, 88-93

src/rpc/methods/eth.rs (1)

880-881: Mechanical switch to accessors is sound.

All tipset resolution and chain_config reads now go through ChainStore accessors; semantics unchanged.

Also applies to: 898-907, 923-926, 940-942, 1637-1639

src/chain/store/chain_store.rs (2)

241-254: New ChainStore accessors are consistent and minimal.

Returning &Arc for blockstore and exposing chain_index()/chain_config() is coherent with the rest of the refactor.


342-343: Using ChainIndex::db() for genesis timestamp is appropriate.

This avoids reaching into private fields and preserves behavior.

src/state_manager/mod.rs (3)

180-182: StateManager constructors updated correctly.

Pulling beacon/config via ChainStore accessors matches the new ownership model.

Also applies to: 189-197


275-283: Accessor and Arc ergonomics LGTM.

blockstore() returning &Arc, blockstore_owned(), and chain_index()/chain_config() accessors are used correctly.

Also applies to: 289-297, 299-306


1675-1679: Good: next-tipset lookup uses correct mapping.

try_lookup_state_from_next_tipset correctly derives (state_root, receipt_root) from the next tipset’s parent_state and message_receipts.

src/daemon/mod.rs (4)

11-11: Import looks correct.

Needed for the new warmup logic using ResolveNullTipset.


42-42: Import looks correct.

Instant is used in the warmup timer.


520-520: Good: switched to chain_store.blockstore() for the collector.

Aligns with the privatized fields and avoids direct DB coupling.


613-613: Orphaned background task: warmup is not tracked by JoinSet.

warmup_in_background spawns a blocking task that isn't added to services. If start_services is restarted (e.g., GC-triggered reboot loop), prior warmups keep running, leading to duplicated work and leaked tasks holding ChainStore alive.

Apply this diff to attach the task to services:

-    warmup_in_background(&ctx);
+    warmup_in_background(&ctx, &mut services);

Likely an incorrect or invalid review comment.

Comment on lines 631 to 653
fn warmup_in_background(ctx: &AppContext) {
// Populate `tipset_by_height` cache
let cs = ctx.chain_store().clone();
tokio::task::spawn_blocking(move || {
let start = Instant::now();
match cs.chain_index().tipset_by_height(
// 0 would short-curcuit the cache
1,
cs.heaviest_tipset(),
ResolveNullTipset::TakeOlder,
) {
Ok(_) => {
tracing::warn!(
"Successfully populated tipset_by_height cache, took {}",
humantime::format_duration(start.elapsed())
);
}
Err(e) => {
tracing::warn!("Failed to populate tipset_by_height cache: {e}");
}
}
});
}
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Manage warmup task lifecycle; use info-level on success; fix typo.

  • Track the spawned task via JoinSet to avoid leaks across restarts.
  • Log success at info! (not warn!).
  • Fix “short-curcuit” → “short-circuit”.
  • Optional: bound the warmup range to a finality-sized window to reduce IO and align with your prior preference for finality-bounded caching. This keeps fork safety while avoiding a full walk from head to height 1.

Apply this diff:

-fn warmup_in_background(ctx: &AppContext) {
-    // Populate `tipset_by_height` cache
-    let cs = ctx.chain_store().clone();
-    tokio::task::spawn_blocking(move || {
-        let start = Instant::now();
-        match cs.chain_index().tipset_by_height(
-            // 0 would short-curcuit the cache
-            1,
-            cs.heaviest_tipset(),
-            ResolveNullTipset::TakeOlder,
-        ) {
-            Ok(_) => {
-                tracing::warn!(
-                    "Successfully populated tipset_by_height cache, took {}",
-                    humantime::format_duration(start.elapsed())
-                );
-            }
-            Err(e) => {
-                tracing::warn!("Failed to populate tipset_by_height cache: {e}");
-            }
-        }
-    });
-}
+fn warmup_in_background(ctx: &AppContext, services: &mut JoinSet<anyhow::Result<()>>) {
+    // Populate `tipset_by_height` cache.
+    let cs = ctx.chain_store().clone();
+    services.spawn(async move {
+        // Run off the async executor; don't block the runtime.
+        let joinres = tokio::task::spawn_blocking(move || {
+            let start = Instant::now();
+            // Height 0 short-circuits and doesn't populate the cache.
+            match cs.chain_index().tipset_by_height(
+                1,
+                cs.heaviest_tipset(),
+                ResolveNullTipset::TakeOlder,
+            ) {
+                Ok(_) => {
+                    tracing::info!(
+                        "Populated tipset_by_height cache in {}",
+                        humantime::format_duration(start.elapsed())
+                    );
+                }
+                Err(e) => {
+                    tracing::warn!("Failed to populate tipset_by_height cache: {e}");
+                }
+            }
+        }).await;
+        if let Err(e) = joinres {
+            tracing::warn!("Warmup task panicked: {e}");
+        }
+        Ok(())
+    });
+}

Optional (no diff): bound the warmup to a finality window (e.g., call tipset_by_height(head_epoch - FINALITY, head, …)), or gate via a config flag for resource‑constrained environments.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn warmup_in_background(ctx: &AppContext) {
// Populate `tipset_by_height` cache
let cs = ctx.chain_store().clone();
tokio::task::spawn_blocking(move || {
let start = Instant::now();
match cs.chain_index().tipset_by_height(
// 0 would short-curcuit the cache
1,
cs.heaviest_tipset(),
ResolveNullTipset::TakeOlder,
) {
Ok(_) => {
tracing::warn!(
"Successfully populated tipset_by_height cache, took {}",
humantime::format_duration(start.elapsed())
);
}
Err(e) => {
tracing::warn!("Failed to populate tipset_by_height cache: {e}");
}
}
});
}
fn warmup_in_background(ctx: &AppContext, services: &mut JoinSet<anyhow::Result<()>>) {
// Populate `tipset_by_height` cache.
let cs = ctx.chain_store().clone();
services.spawn(async move {
// Run off the async executor; don't block the runtime.
let joinres = tokio::task::spawn_blocking(move || {
let start = Instant::now();
// Height 0 short-circuits and doesn't populate the cache.
match cs.chain_index().tipset_by_height(
1,
cs.heaviest_tipset(),
ResolveNullTipset::TakeOlder,
) {
Ok(_) => {
tracing::info!(
"Populated tipset_by_height cache in {}",
humantime::format_duration(start.elapsed())
);
}
Err(e) => {
tracing::warn!("Failed to populate tipset_by_height cache: {e}");
}
}
}).await;
if let Err(e) = joinres {
tracing::warn!("Warmup task panicked: {e}");
}
Ok(())
});
}
🤖 Prompt for AI Agents
In src/daemon/mod.rs around lines 631-653, the warmup_in_background routine
currently spawns a blocking task without tracking it, logs a successful warmup
at warn! and contains a typo; change it to spawn and record the task in a
JoinSet (or otherwise store the JoinHandle in the daemon/AppContext so it can be
awaited/aborted on restart) instead of fire-and-forget, change the successful
branch to tracing::info! (not tracing::warn!), fix the comment typo
“short-curcuit” → “short-circuit”, and—optionally—limit the tipset_by_height
walk to a finality-sized window (e.g., head_epoch - FINALITY) or gate the warmup
behind a config flag to reduce IO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed log level

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hanabi1224, thanks for fixing the log level! 🐰

I see you've addressed the tracing::warn!tracing::info! change for the success case. The other items from my review (task lifecycle management via JoinSet and the "short-circuit" typo) can be addressed separately if needed.

The warmup functionality looks good overall - it's a nice performance optimization for startup! ✨

(´。• ᵕ •。`) ♡


🧠 Learnings used
Learnt from: hanabi1224
PR: ChainSafe/forest#5930
File: build.rs:64-77
Timestamp: 2025-08-13T09:43:20.301Z
Learning: hanabi1224 prefers hard compile-time errors in build scripts rather than runtime safeguards or collision detection, believing it's better to fail fast and fix root causes of issues like malformed snapshot names.

Learnt from: hanabi1224
PR: ChainSafe/forest#5944
File: src/chain/store/index.rs:0-0
Timestamp: 2025-08-18T03:09:47.932Z
Learning: In Forest's tipset_by_height caching implementation, hanabi1224 prefers performance-conscious solutions that leverage finality guarantees rather than expensive chain walking for fork detection. The approach of constraining cache lookups to finalized epochs (using CHECKPOINT_INTERVAL >= CHAIN_FINALITY) provides fork safety without the performance cost of ancestry verification.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo fixed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hanabi1224, perfect! Thanks for fixing the typo as well. 🎯

With both the log level and typo addressed, the warmup function looks much cleaner now. The remaining task lifecycle management (tracking via JoinSet) is more of an architectural consideration and can be addressed separately if you decide it's worthwhile for your use case.

The cache warmup is a great addition to improve startup performance! 🚀


🧠 Learnings used
Learnt from: hanabi1224
PR: ChainSafe/forest#5944
File: src/chain/store/index.rs:0-0
Timestamp: 2025-08-18T03:09:47.932Z
Learning: In Forest's tipset_by_height caching implementation, hanabi1224 prefers performance-conscious solutions that leverage finality guarantees rather than expensive chain walking for fork detection. The approach of constraining cache lookups to finalized epochs (using CHECKPOINT_INTERVAL >= CHAIN_FINALITY) provides fork safety without the performance cost of ancestry verification.

Learnt from: hanabi1224
PR: ChainSafe/forest#5930
File: build.rs:64-77
Timestamp: 2025-08-13T09:43:20.301Z
Learning: hanabi1224 prefers hard compile-time errors in build scripts rather than runtime safeguards or collision detection, believing it's better to fail fast and fix root causes of issues like malformed snapshot names.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/daemon/mod.rs (2)

612-612: Track the warmup task via JoinSet to avoid fire‑and‑forget.
Prevents leaks across service restarts and surfaces panics. Apply with the function change below.

-    warmup_in_background(&ctx);
+    warmup_in_background(&ctx, &mut services);

631-653: Manage warmup task lifecycle; handle panics; fix typo.

  • Use JoinSet and join the blocking task to catch panics.
  • Typo: “short-curcuit” → “short-circuit”.
-fn warmup_in_background(ctx: &AppContext) {
-    // Populate `tipset_by_height` cache
-    let cs = ctx.chain_store().clone();
-    tokio::task::spawn_blocking(move || {
-        let start = Instant::now();
-        match cs.chain_index().tipset_by_height(
-            // 0 would short-curcuit the cache
-            1,
-            cs.heaviest_tipset(),
-            ResolveNullTipset::TakeOlder,
-        ) {
-            Ok(_) => {
-                tracing::info!(
-                    "Successfully populated tipset_by_height cache, took {}",
-                    humantime::format_duration(start.elapsed())
-                );
-            }
-            Err(e) => {
-                tracing::warn!("Failed to populate tipset_by_height cache: {e}");
-            }
-        }
-    });
-}
+fn warmup_in_background(ctx: &AppContext, services: &mut JoinSet<anyhow::Result<()>>) {
+    // Populate `tipset_by_height` cache.
+    let cs = ctx.chain_store().clone();
+    services.spawn(async move {
+        // Run off the async executor; don't block the runtime.
+        let joinres = tokio::task::spawn_blocking(move || {
+            let start = Instant::now();
+            match cs.chain_index().tipset_by_height(
+                // 0 would short-circuit the cache
+                1,
+                cs.heaviest_tipset(),
+                ResolveNullTipset::TakeOlder,
+            ) {
+                Ok(_) => {
+                    tracing::info!(
+                        "Populated tipset_by_height cache in {}",
+                        humantime::format_duration(start.elapsed())
+                    );
+                }
+                Err(e) => {
+                    tracing::warn!("Failed to populate tipset_by_height cache: {e}");
+                }
+            }
+        }).await;
+        if let Err(e) = joinres {
+            tracing::warn!("Warmup task panicked: {e}");
+        }
+        Ok(())
+    });
+}
🧹 Nitpick comments (2)
src/daemon/mod.rs (2)

226-229: Switch to blockstore handle is correct; consider avoiding per-scrape StateTree rebuild.
The API change is wired correctly. Minor perf: constructing StateTree on every metrics scrape can be heavy; cache the actor version (e.g., OnceCell updated on head-change) and read it here.


631-653: Bound warmup to the chain-finality window or make it configurable.
Starting at height 1 is IO-heavy — limit warmup to [head_epoch − chain_finality, head_epoch] or add a config toggle. Change src/daemon/mod.rs::warmup_in_background to compute start = max(1, cs.heaviest_tipset().epoch() - chain_config.policy.chain_finality) and pass that to tipset_by_height instead of 1. ChainConfig::policy.chain_finality is available (see src/networks/mod.rs and usages in src/f3/mod.rs and src/chain/store/chain_store.rs).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40a8502 and 019b673.

📒 Files selected for processing (1)
  • src/daemon/mod.rs (6 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: hanabi1224
PR: ChainSafe/forest#5944
File: src/chain/store/index.rs:0-0
Timestamp: 2025-08-18T03:09:47.932Z
Learning: In Forest's tipset_by_height caching implementation, hanabi1224 prefers performance-conscious solutions that leverage finality guarantees rather than expensive chain walking for fork detection. The approach of constraining cache lookups to finalized epochs (using CHECKPOINT_INTERVAL >= CHAIN_FINALITY) provides fork safety without the performance cost of ancestry verification.
Learnt from: hanabi1224
PR: ChainSafe/forest#5930
File: build.rs:64-77
Timestamp: 2025-08-13T09:43:20.301Z
Learning: hanabi1224 prefers hard compile-time errors in build scripts rather than runtime safeguards or collision detection, believing it's better to fail fast and fix root causes of issues like malformed snapshot names.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: tests
  • GitHub Check: tests-release
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: Build Ubuntu
  • GitHub Check: Build MacOS
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: All lint checks
🔇 Additional comments (3)
src/daemon/mod.rs (3)

11-11: LGTM: correct import for null-tipset resolution.
Needed for ResolveNullTipset::TakeOlder in warmup.


42-42: LGTM: Instant import is appropriate for timing the warmup.


520-520: LGTM: pass blockstore to EthMappingCollector::new.
Matches the refactor away from direct DB handle access.

@hanabi1224 hanabi1224 force-pushed the hm/populate_tipset_by_height_cache branch from 019b673 to 42dfb6f Compare September 19, 2025 12:00
Copy link
Member

@LesnyRumcajs LesnyRumcajs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this increase startup time?

@hanabi1224
Copy link
Contributor Author

Does this increase startup time?

@LesnyRumcajs No, the warmup task runs in background

@LesnyRumcajs
Copy link
Member

@akaladarshi

@LesnyRumcajs LesnyRumcajs added this pull request to the merge queue Sep 23, 2025
Merged via the queue into main with commit 35b1cf8 Sep 23, 2025
63 of 64 checks passed
@LesnyRumcajs LesnyRumcajs deleted the hm/populate_tipset_by_height_cache branch September 23, 2025 11:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants