Skip to content

Conversation

@akaladarshi
Copy link
Collaborator

@akaladarshi akaladarshi commented Dec 3, 2025

Summary of changes

Changes introduced in this pull request:

  • Store FVM events in blockstore
  • Removed the indices store column from the DB, so now it doesn't store the events root -> tipsetKey mapping
  • Directly using the blockstore to serve the ChainGetEvents API, and if events are not available we just error out, same as lotus

Reference issue to close (if applicable)

Closes #6271

Other information and links

The issue was: A message generated two identical events and forest always used to compute the state and get the events for serving the ChainGetEvents API call request, so we were always getting two identical events, but the lotus was serving directly from the blockstore so they were getting single event.

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

  • New Features

    • Direct event retrieval path with improved cross-version event handling and untagged serialization support.
  • Bug Fixes

    • Events now preserve exact order and duplicates; clearer mismatch error messages.
  • Tests

    • Expanded coverage for event storage, ordering, duplicates, and multi-version compatibility.
  • Chores

    • Removed legacy event-root indexing/backfill paths and simplified internal store initialization.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 3, 2025

Walkthrough

Removed IndicesStore and its usages, shortened ChainStore::new (dropped indices/db arg), switched async chain-event collection to a non-async get_events_by_event_root via StampedEvent::get_events, added event AMT storage/validation in StateManager, and expanded event tests for V3/V4, order, duplicates.

Changes

Cohort / File(s) Change Summary
Chain store constructor & indices removal
src/chain/store/chain_store.rs, src/db/mod.rs, src/db/memory.rs, src/db/parity_db.rs, src/db/car/many.rs
Removed IndicesStore trait and implementations; removed indices field/usage from ChainStore; updated ChainStore::new signature to drop the indices/db argument and removed related methods (put_index, get_tipset_key).
Call-site updates for ChainStore::new
src/daemon/context.rs, src/daemon/db_util.rs, src/benchmark_private/tipset_validation.rs, src/chain_sync/chain_follower.rs, src/libp2p/chain_exchange/provider.rs, src/rpc/methods/sync.rs, src/tool/* (.../api_cmd/*, index_cmd.rs, state_compute_cmd.rs, offline_server/server.rs, test_snapshot.rs, generate_test_snapshot.rs, api_compare_tests.rs)
Removed extra db.clone() / writer argument at ChainStore construction sites and adjusted call sites; removed indices backfill/copy logic where present; some call sites now propagate Result (?) instead of unwrap.
Event retrieval API & dedup semantics
src/rpc/methods/chain.rs, src/rpc/methods/eth/filter/mod.rs
Replaced async chain-event collection with direct get_events_by_event_root using StampedEvent::get_events; removed previous deduplication so AMT order and duplicates are preserved; error messages include concrete lengths.
StampedEvent API and AMT support
src/shim/executor.rs
Added #[serde(untagged)] to StampedEvent; added StampedEvent::get_events to load events from an events-AMT root with V4→V3 fallback; broadened Amt imports.
State manager: event storage/validation & public types
src/state_manager/mod.rs, src/state_manager/tests.rs
Added EVENTS_AMT_BITWIDTH constant and public StateEvents struct; changed tipset_state_events to compute/fallback events and removed writing of events index; during apply, derive/store events AMTs and ensure! that derived root matches provided root; added extensive V3/V4 and ordering/duplicate tests.
Tooling / snapshots
src/tool/subcommands/api_cmd/test_snapshots.txt
Updated filecoin_chaingetevents snapshot entries (replaced four snapshot filenames with four new ones).

Sequence Diagram(s)

sequenceDiagram
    participant RPC as RPC Handler
    participant SM as StateManager
    participant Exec as Executor / StampedEvent
    participant BS as BlockStore
    participant AMT as AMT

    Note over SM,Exec: Apply — build and store events AMTs
    RPC->>SM: apply_block_messages(messages, events)
    SM->>Exec: compute state & receipts
    SM->>AMT: build receipts AMT (Amtv0)
    SM->>AMT: derive events AMT(s) (Amt with EVENTS_AMT_BITWIDTH)
    SM->>SM: ensure(derived_root == provided_event_root)
    SM->>BS: put events AMT root and nodes

    Note over RPC,SM: Retrieve — load by events root or compute fallback
    RPC->>SM: tipset_state_events(tipset) or get_events_by_event_root(root)
    alt events stored
        SM->>BS: load events AMT by root
        BS->>Exec: AMT node data
        Exec->>Exec: StampedEvent::get_events(root) (try V4, fallback V3)
        Exec-->>SM: Vec<Event> (order & duplicates preserved)
    else compute fallback
        SM->>Exec: compute state & extract events
        Exec-->>SM: Vec<Event>
    end
    SM-->>RPC: StateEvents / events
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas to focus during review:
    • ChainStore::new signature change and all updated call-sites for correct argument ordering and error handling.
    • Complete removal of IndicesStore trait and any remaining imports/usages.
    • StampedEvent::get_events fallback logic across V4/V3 AMT layouts and serde behavior.
    • Event AMT derivation and ensure! validation in apply flow (consistency with provided roots).
    • RPC/event path changes to ensure consumers expect preserved order/duplicates.
    • New/updated tests in src/state_manager/tests.rs for coverage and stability.

Possibly related PRs

Suggested reviewers

  • hanabi1224
  • LesnyRumcajs
  • sudo-shashank

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR removes the entire IndicesStore infrastructure across multiple database implementations and files, which extends beyond fixing the ChainGetEvents deduplication logic. The PR removes IndicesStore trait, database columns, and implementations across 8+ files, which is a broader refactoring than fixing the ChainGetEvents API. Consider separating IndicesStore removal into a separate PR.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: removing the unique/deduplication logic from ChainGetEvents API and aligning with Lotus behavior.
Linked Issues check ✅ Passed The PR addresses the RPC parity test failure by removing duplicate event logic and serving events directly from blockstore, aligning Forest with Lotus behavior as required.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch akaladarshi/fix-get-events-api

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

@akaladarshi akaladarshi force-pushed the akaladarshi/fix-get-events-api branch 3 times, most recently from dda3266 to a15e4e9 Compare December 4, 2025 16:29
@akaladarshi akaladarshi added the RPC requires calibnet RPC checks to run on CI label Dec 4, 2025
@akaladarshi akaladarshi marked this pull request as ready for review December 4, 2025 16:30
@akaladarshi akaladarshi requested a review from a team as a code owner December 4, 2025 16:30
@akaladarshi akaladarshi requested review from LesnyRumcajs and hanabi1224 and removed request for a team December 4, 2025 16: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

🧹 Nitpick comments (3)
src/shim/executor.rs (1)

340-415: StampedEvent AMT loader looks good; watch serde wire format & small perf nit

The new StampedEvent::get_events helper correctly handles both v4-first and v3-fallback AMT loading and preserves storage order, which aligns with the new state-manager tests. Two minor follow‑ups to consider:

  • #[serde(untagged)] on StampedEvent changes its serialized shape (no variant tag). Please double‑check that nothing persists or exchanges the old tagged representation; if there is such usage, a compatibility note or migration test would help.
  • If you expect large event AMTs, you can avoid reallocations by reserving events capacity from the AMT’s element count before for_each.
src/state_manager/tests.rs (1)

341-406: Cache tests are correct; consider aligning events_root construction with production

The receipt/events cache tests behave correctly: update_cache_with_state_output only uses the provided events vector and treats events_root as an opaque identifier, so building the events root with Amtv0 is safe here. For future clarity (especially now that production writes events into a specific-bitwidth AMT), it may be worth switching this test’s events_root construction to the same AMT flavor/bitwidth used in the main code, or adding a comment noting that the root isn’t dereferenced in this test.

src/state_manager/mod.rs (1)

99-105: StateEvents and cache update logic look coherent; consider asserting vector invariants

Making StateEvents public and wiring update_cache_with_state_output to insert both events and their roots into the receipt/event cache handler matches how tipset_state_events and the RPC layer consume this data. One implicit assumption is that events.len() and events_roots.len() always stay equal; given other code zips these two vectors, it may be worth adding a debug/assert (either when creating StateEvents or before caching) to catch any future mismatch early.

Also applies to: 511-528

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07cbdaf and a15e4e9.

📒 Files selected for processing (7)
  • src/chain/store/chain_store.rs (1 hunks)
  • src/rpc/methods/chain.rs (1 hunks)
  • src/rpc/methods/eth/filter/mod.rs (1 hunks)
  • src/shim/executor.rs (3 hunks)
  • src/state_manager/mod.rs (4 hunks)
  • src/state_manager/tests.rs (4 hunks)
  • src/tool/subcommands/api_cmd/test_snapshots.txt (1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-09-02T14:23:53.808Z
Learnt from: akaladarshi
Repo: ChainSafe/forest PR: 5923
File: src/tool/subcommands/api_cmd/test_snapshots.txt:206-252
Timestamp: 2025-09-02T14:23:53.808Z
Learning: In the Forest project, .rpcsnap.json.zst snapshot files listed in src/tool/subcommands/api_cmd/test_snapshots.txt are not stored in the repository itself but are downloaded from a DigitalOcean (DO) bucket when needed. The manifest file serves as an index/catalog of available snapshots.

Applied to files:

  • src/tool/subcommands/api_cmd/test_snapshots.txt
📚 Learning: 2025-08-25T14:11:10.790Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5978
File: .github/workflows/unit-tests.yml:86-96
Timestamp: 2025-08-25T14:11:10.790Z
Learning: hanabi1224's RPC snapshot tests include a checksum validation mechanism that compares against HTTP HEAD headers, automatically re-downloading snapshots if the cached version doesn't match, providing protection against corrupted cached files.

Applied to files:

  • src/tool/subcommands/api_cmd/test_snapshots.txt
📚 Learning: 2025-07-17T15:21:40.753Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5841
File: src/utils/get_size/mod.rs:10-10
Timestamp: 2025-07-17T15:21:40.753Z
Learning: The get_size2 crate's GetSize trait provides default implementations: get_stack_size() uses std::mem::size_of, get_heap_size() returns 0, and get_size() returns their sum. An empty impl like `impl GetSize for MyType {}` is valid and uses these defaults, making it suitable for simple wrapper types that don't allocate heap memory.

Applied to files:

  • src/shim/executor.rs
📚 Learning: 2025-08-08T12:11:55.266Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 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/rpc/methods/chain.rs
📚 Learning: 2025-09-11T16:03:14.328Z
Learnt from: akaladarshi
Repo: ChainSafe/forest PR: 6000
File: src/tool/subcommands/api_cmd/state_decode_params_tests/miner.rs:4-9
Timestamp: 2025-09-11T16:03:14.328Z
Learning: In the Forest codebase's state_decode_params_tests module, common imports like `to_vec`, `TokenAmount`, `Cid`, etc. are centralized in the mod.rs file and made available to submodules through `use super::*;`, eliminating the need for duplicate imports in individual test files.

Applied to files:

  • src/state_manager/tests.rs
  • src/state_manager/mod.rs
🧬 Code graph analysis (2)
src/chain/store/chain_store.rs (2)
src/shim/executor.rs (1)
  • key (305-310)
src/blocks/tipset.rs (2)
  • key (337-340)
  • key (512-515)
src/state_manager/tests.rs (1)
src/shim/executor.rs (4)
  • events_root (180-186)
  • event (383-388)
  • events (118-124)
  • get_events (392-414)
⏰ 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: cargo-publish-dry-run
  • GitHub Check: Build MacOS
  • GitHub Check: Build Ubuntu
  • GitHub Check: tests-release
  • GitHub Check: tests
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: All lint checks
🔇 Additional comments (7)
src/tool/subcommands/api_cmd/test_snapshots.txt (1)

7-7: The snapshot file filecoin_chaingetevents_1764864316078100.rpcsnap.json.zst is correctly listed in the manifest and is already available in the DigitalOcean bucket (https://forest-snapshots.fra1.cdn.digitaloceanspaces.com/rpc_test/). The change is valid and the test suite will be able to download this snapshot at runtime.

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

379-395: Preserving AMT event duplicates and order is correct

The updated collect_chain_events flow (filter by root, then flatten without dedup) now faithfully reflects the AMT contents, including duplicates and order, which is consistent with the new events tests and the API goal of returning all emitted events.

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

201-208: Renamed tipset-key lookup clarifies events-root usage

The new get_tipset_key_by_events_root wrapper keeps the previous behavior while making the intent (lookup by events root) explicit, which matches its use from ChainGetEvents.

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

254-263: ChainGetEvents now correctly resolves via events-root index

Switching to get_tipset_key_by_events_root and updating the error message to reference the tipset for the given events root aligns the RPC with the new indexing logic; the subsequent tipset load and event collection remain consistent.

src/state_manager/mod.rs (3)

471-595: Event-root indexing and blockstore fallback path are sensible; confirm all relevant tipsets pass through here

The new logic in tipset_state_output and tipset_state_events gives a clear pipeline:

  • When computing state, you index each non‑None events_root to its TipsetKey.
  • When serving events, you first try the cache, then load from the events AMT in the blockstore (when an events_root is provided), and only fall back to recomputing state if that fails.

This is a good layering and matches how ChainGetEvents now resolves root → tipset. One thing to verify is that, for all tipsets whose events you expect clients to query by root, there is at least one code path that calls tipset_state_output (and thus writes these indices). If some validation or background paths only ever call apply_block_messages directly without going through tipset_state_output, their events_root mappings won’t be written and ChainGetEvents for those particular roots will correctly, but perhaps unexpectedly, return the “can't find tipset for events root …” error.


1898-2034: Receipt and events AMT construction/verification in apply_block_messages is well-structured

Within apply_block_messages, constructing the receipts root from the VM‑returned receipts and then, separately, materializing each non‑empty events AMT and asserting that its derived root matches the VM‑provided root gives you:

  • A receipts root consistent with what Receipt::get_receipts expects to read.
  • Events AMTs that are actually persisted, with an explicit equality check guarding against any divergence between how the VM computes roots and how the node rebuilds them (including bitwidth configuration).

The error path on an events‑root mismatch is clear and will surface early during tipset execution, which is appropriate for a consensus‑sensitive invariant.


1769-1819: validate_tipsets now bails cleanly on state mismatch

Switching validate_tipsets to use bail!("state mismatch") when the expected and actual state/receipt roots differ simplifies the error path without changing behavior, and the surrounding logging still records all the relevant details for debugging.

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

🧹 Nitpick comments (1)
src/state_manager/tests.rs (1)

437-625: Event AMT tests comprehensively cover duplicates, order, empties, and v3/v4 compatibility

The new helpers and tests look solid:

  • create_raw_event_v3/v4 generate deterministic RAW IPLD events, which is ideal for CID equality checks.
  • test_events_store_and_retrieve_basic, test_events_entries_are_preserved_when_duplicates_are_stored, and test_events_preserve_order directly exercise StampedEvent::get_events and confirm the “no dedup, preserve insertion order” behavior that this PR is aiming for.
  • test_events_same_content_same_cid and test_identical_events_produce_same_root together validate that identical event sequences yield identical AMT roots and remain readable through get_events.
  • test_events_empty_list and test_events_v3_store_and_retrieve nicely cover the empty‑root and v3‑compat paths.

One small, purely optional nit: test_events_same_content_same_cid and test_identical_events_produce_same_root overlap quite a bit; you could fold them into a single test that asserts both CID equality and successful retrieval to reduce duplication, but it's not required.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a15e4e9 and b50f882.

📒 Files selected for processing (3)
  • scripts/tests/api_compare/filter-list (0 hunks)
  • scripts/tests/api_compare/filter-list-offline (0 hunks)
  • src/state_manager/tests.rs (4 hunks)
💤 Files with no reviewable changes (2)
  • scripts/tests/api_compare/filter-list-offline
  • scripts/tests/api_compare/filter-list
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-11T16:03:14.328Z
Learnt from: akaladarshi
Repo: ChainSafe/forest PR: 6000
File: src/tool/subcommands/api_cmd/state_decode_params_tests/miner.rs:4-9
Timestamp: 2025-09-11T16:03:14.328Z
Learning: In the Forest codebase's state_decode_params_tests module, common imports like `to_vec`, `TokenAmount`, `Cid`, etc. are centralized in the mod.rs file and made available to submodules through `use super::*;`, eliminating the need for duplicate imports in individual test files.

Applied to files:

  • src/state_manager/tests.rs
🧬 Code graph analysis (1)
src/state_manager/tests.rs (1)
src/shim/executor.rs (4)
  • events_root (180-186)
  • event (383-388)
  • events (118-124)
  • get_events (392-414)
⏰ 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: cargo-publish-dry-run
  • GitHub Check: Build Ubuntu
  • GitHub Check: Build MacOS
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: tests
  • GitHub Check: tests-release
  • GitHub Check: All lint checks
🔇 Additional comments (2)
src/state_manager/tests.rs (2)

14-14: Importing both Amt and Amtv0 keeps tests aligned with runtime AMT variants

Using Amt for the new EVENTS_AMT_BITWIDTH‑backed event AMTs and Amtv0 for legacy roots (receipts / earlier tests) makes the intent explicit and avoids alias confusion; usage below is consistent with this split. No changes needed here, just noting the clarity.


351-352: Amtv0::new_from_iter correctly mirrors v0‑formatted roots in cache tests

Switching both the mock events_root and receipt_root construction to Amtv0::new_from_iter ensures these tests only pass when the blockstore actually contains a valid v0 AMT, matching how roots are stored/loaded in the state manager. This gives the cache tests more realistic coverage without overcomplicating them.

Also applies to: 388-389

@akaladarshi akaladarshi marked this pull request as draft December 4, 2025 17:04
@akaladarshi akaladarshi force-pushed the akaladarshi/fix-get-events-api branch from b1ae8f4 to ad77eee Compare December 5, 2025 12:21
@akaladarshi akaladarshi marked this pull request as ready for review December 5, 2025 12:48
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

Caution

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

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

511-528: tipset_state_events fast path currently poisons the events cache with partial data

When events_root is provided, the new logic:

  • Checks the cache via get_events_or_else.
  • Inside the supplier closure, tries to load only the events for that root via StampedEvent::get_events, and if found, returns a StateEvents containing a single Vec<StampedEvent> and a single Some(root).

Because this StateEvents is then cached under the tipset key, later callers that expect a full tipset view (e.g., collect_events with events_root = None) will:

  • Pull this truncated StateEvents from the cache.
  • Hit the new length check in collect_events (messages.len() == events.len()), and fail with a length mismatch.

This can manifest whenever Filecoin.ChainGetEvents is called for a tipset before any filter-based event queries.

A safer pattern would be:

  • Use the blockstore fast path outside of get_events_or_else and do not insert that partial StateEvents into the cache, or
  • Restrict the cache to always store the full per-tipset events/roots produced by compute_tipset_state, regardless of whether an events_root hint is present.

For example, you could restructure tipset_state_events roughly as:

@@
-        let key = tipset.key();
-        let ts = tipset.clone();
-        let this = Arc::clone(self);
-        let cids = tipset.cids();
-        let events_root = events_root.cloned();
-        self.receipt_event_cache_handler
-            .get_events_or_else(
-                key,
-                Box::new(move || {
-                    Box::pin(async move {
-                        // Try to load events directly from the blockstore
-                        if let Some(stamped_events) = events_root
-                            .as_ref()
-                            .and_then(|root| StampedEvent::get_events(this.blockstore(), root).ok())
-                            .filter(|events| !events.is_empty())
-                        {
-                            return Ok(StateEvents {
-                                events: vec![stamped_events],
-                                roots: vec![events_root],
-                            });
-                        }
-
-                        // Fallback: compute the tipset state if events not found in the blockstore
-                        let state_out = this
-                            .compute_tipset_state(ts, NO_CALLBACK, VMTrace::NotTraced)
-                            .await?;
-                        trace!("Completed tipset state calculation {:?}", cids);
-                        Ok(StateEvents {
-                            events: state_out.events,
-                            roots: state_out.events_roots,
-                        })
-                    })
-                }),
-            )
-            .await
+        let events_root = events_root.cloned();
+
+        // Fast path: if a specific events root is requested and the AMT exists,
+        // serve it directly without mutating the per‑tipset cache.
+        if let Some(root) = events_root.as_ref() {
+            if let Ok(stamped_events) = StampedEvent::get_events(self.blockstore(), root) {
+                if !stamped_events.is_empty() {
+                    return Ok(StateEvents {
+                        events: vec![stamped_events],
+                        roots: vec![events_root],
+                    });
+                }
+            }
+        }
+
+        let key = tipset.key();
+        let ts = tipset.clone();
+        let this = Arc::clone(self);
+        let cids = tipset.cids();
+        self.receipt_event_cache_handler
+            .get_events_or_else(
+                key,
+                Box::new(move || {
+                    Box::pin(async move {
+                        let state_out = this
+                            .compute_tipset_state(ts, NO_CALLBACK, VMTrace::NotTraced)
+                            .await?;
+                        trace!("Completed tipset state calculation {:?}", cids);
+                        Ok(StateEvents {
+                            events: state_out.events,
+                            roots: state_out.events_roots,
+                        })
+                    })
+                }),
+            )
+            .await

This keeps the cache invariant (“cached StateEvents represent the full tipset”) while still leveraging the AMT-backed fast path for ChainGetEvents.

Also applies to: 555-595


199-205: Verify that event index insertion is not bypassed in future state computation paths

tipset_state_output indexes events roots via put_index after computing state, but this indexing only occurs in two paths:

  • Main RPC path via tipset_state_output (line 497)
  • Backfill utility via process_ts in db_util.rs

Direct calls to compute_tipset_state or apply_block_messages outside these two paths—such as in execution_trace, validation, or tool commands—do not trigger indexing. While current production paths (sync via tipset_syncer, RPC via tipset_state) route through tipset_state_output properly, future code that calls these functions directly risks creating persisted state without index entries, breaking ChainGetEvents resolution.

Consider either consolidating all state computations through tipset_state_output, or moving the put_index call to the point where events roots are produced (e.g., in apply_block_messages after vm.flush()), to guarantee indexing regardless of caller.

🧹 Nitpick comments (3)
src/rpc/methods/eth/filter/mod.rs (1)

281-291: Stricter message/events length check is appropriate

The added ensure! with concrete lengths will fail fast instead of silently zipping mismatched messages and events, which is much safer for debugging and prevents misaligned event attribution.

src/state_manager/tests.rs (1)

437-625: Event AMT tests give good coverage of storage, order, duplicates, and v3/v4 handling

The new helpers (create_raw_event_v3/v4) and tests comprehensively cover:

  • Basic store/retrieve via EVENTS_AMT_BITWIDTH and StampedEvent::get_events.
  • Duplicate events (consecutive and non‑consecutive) preserving count and position.
  • Strict order preservation across multiple entries.
  • Identical content producing identical root CIDs.
  • Empty-event lists round‑tripping correctly.
  • V3-encoded events still being readable through StampedEvent::get_events.

The slight overlap between test_events_same_content_same_cid and test_identical_events_produce_same_root is acceptable and keeps intent clear.

src/state_manager/mod.rs (1)

1899-2035: Receipt and events AMT construction in apply_block_messages is consistent and well-guarded

The updated apply_block_messages pipeline:

  • Uses Amtv0::new_from_iter to build receipt_root from the receipts vector, aligning with the tests that now construct receipts using Amtv0.
  • Iterates (events, events_roots) and, when an events_root is present, reconstructs the AMT via Amt::new_from_iter_with_bit_width and EVENTS_AMT_BITWIDTH, then ensure!s that the derived root matches the FVM-provided root.

That ensure! gives a clear, early failure if the bitwidth or serialization diverge from what the VM produced, which is valuable for catching subtle incompatibilities at state-application time.

As long as EVENTS_AMT_BITWIDTH stays coordinated with the bitwidth used when events roots are produced, this looks sound.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b50f882 and ad77eee.

📒 Files selected for processing (7)
  • src/chain/store/chain_store.rs (1 hunks)
  • src/rpc/methods/chain.rs (1 hunks)
  • src/rpc/methods/eth/filter/mod.rs (3 hunks)
  • src/shim/executor.rs (3 hunks)
  • src/state_manager/mod.rs (4 hunks)
  • src/state_manager/tests.rs (4 hunks)
  • src/tool/subcommands/api_cmd/test_snapshots.txt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/tool/subcommands/api_cmd/test_snapshots.txt
  • src/shim/executor.rs
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: akaladarshi
Repo: ChainSafe/forest PR: 5923
File: src/rpc/registry/actors/miner.rs:221-223
Timestamp: 2025-09-02T10:05:34.350Z
Learning: For miner actor ChangeOwnerAddress and ChangeOwnerAddressExported methods: versions 8-10 use bare Address as parameter type, while versions 11+ use ChangeOwnerAddressParams. This reflects the evolution of the Filecoin miner actor parameter structures across versions.
📚 Learning: 2025-09-11T16:03:14.328Z
Learnt from: akaladarshi
Repo: ChainSafe/forest PR: 6000
File: src/tool/subcommands/api_cmd/state_decode_params_tests/miner.rs:4-9
Timestamp: 2025-09-11T16:03:14.328Z
Learning: In the Forest codebase's state_decode_params_tests module, common imports like `to_vec`, `TokenAmount`, `Cid`, etc. are centralized in the mod.rs file and made available to submodules through `use super::*;`, eliminating the need for duplicate imports in individual test files.

Applied to files:

  • src/state_manager/tests.rs
  • src/state_manager/mod.rs
📚 Learning: 2025-08-08T12:11:55.266Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 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/rpc/methods/chain.rs
🧬 Code graph analysis (2)
src/chain/store/chain_store.rs (2)
src/shim/executor.rs (1)
  • key (305-310)
src/blocks/tipset.rs (2)
  • key (337-340)
  • key (512-515)
src/state_manager/mod.rs (2)
src/shim/executor.rs (5)
  • events_root (180-186)
  • key (305-310)
  • new (254-266)
  • get_events (392-414)
  • events (118-124)
src/state_manager/cache.rs (3)
  • get_events (152-152)
  • get_events (206-208)
  • get_events (263-265)
⏰ 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). (3)
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: tests-release
  • GitHub Check: tests
🔇 Additional comments (5)
src/chain/store/chain_store.rs (1)

201-208: Method rename to clarify events-root index usage looks good

Renaming to get_tipset_key_by_events_root better reflects how the indices store is used here; behavior remains a straight passthrough to indices.read_obj and is consistent with the ChainGetEvents call site.

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

248-265: ChainGetEvents now resolves tipset explicitly via events-root index

Switching to get_tipset_key_by_events_root with the updated error context accurately describes the failure mode while keeping the rest of the flow (loading the tipset and collecting events) unchanged.

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

369-403: ChainGetEvents now preserves duplicate events and order as stored in AMT

The new ensure! on state_events.roots.len() == state_events.events.len() plus removal of any deduplication ensures:

  • You only proceed when roots and event vectors are structurally consistent.
  • All events under the selected root (including intentional duplicates) are returned in AMT order.

This aligns with the stated goal of matching Lotus’ behavior for ChainGetEvents.

src/state_manager/tests.rs (1)

340-407: Receipt/events cache tests align with new AMTv0 usage

Using Amtv0::new_from_iter to build receipt_root and the mock events_root matches the production change to use Amtv0 for receipts while treating event roots as opaque Cids in the cache. The tests correctly assert that empty events do not populate the cache and that a valid AMT-backed receipt_root yields one cached receipt.

src/state_manager/mod.rs (1)

93-105: StateEvents layout and sizing helpers look consistent

Making StateEvents public with events: Vec<Vec<StampedEvent>> and roots: Vec<Option<Cid>>, plus the GetSize annotations, matches how it’s consumed by the cache layer and RPC code. The explicit heap-size helpers for nested vectors should keep cache accounting accurate.

@akaladarshi akaladarshi marked this pull request as draft December 5, 2025 14:32
@akaladarshi akaladarshi force-pushed the akaladarshi/fix-get-events-api branch from 17bdc74 to 51357fe Compare December 9, 2025 14:24
@akaladarshi akaladarshi force-pushed the akaladarshi/fix-get-events-api branch from 51357fe to 35ac348 Compare December 9, 2025 14:35
@akaladarshi
Copy link
Collaborator Author

akaladarshi commented Dec 9, 2025

DB migration check in CI is failing because I've removed the Indices column from DB so need to create a new migration or maybe go back to old not sure right now.

I was thinking, if I can get some comments on the PR, to finalise before the migration, it would be helpful 🙏 @hanabi1224 @LesnyRumcajs

@akaladarshi akaladarshi marked this pull request as ready for review December 9, 2025 15:13
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

🧹 Nitpick comments (2)
src/state_manager/mod.rs (1)

1985-2006: Consider adding a length check before the zip operation.

The zip at line 1989 will silently truncate if events and events_roots have different lengths. While apply_block_messages should return consistent lengths, adding an explicit check would catch bugs early.

         // step 5: construct receipt root from receipts
         let receipt_root = Amtv0::new_from_iter(chain_index.db(), receipts)?;

         // step 6: store events AMTs in the blockstore
+        ensure!(
+            events.len() == events_roots.len(),
+            "Events count ({}) and events_roots count ({}) mismatch",
+            events.len(),
+            events_roots.len()
+        );
         for (msg_events, events_root) in events.iter().zip(events_roots.iter()) {
src/rpc/methods/eth/filter/mod.rs (1)

368-383: LGTM on get_events_by_event_root implementation.

The function correctly loads events via StampedEvent::get_events from the blockstore without deduplication, aligning with Lotus behavior. The error message at line 377 provides useful context.

Consider simplifying the error handling pattern:

-    let state_events =
-        match StampedEvent::get_events(ctx.chain_store().blockstore(), events_root) {
-            Ok(e) => e,
-            Err(e) => {
-                return Err(anyhow::anyhow!("load events amt: {}", e));
-            }
-        };
+    let state_events = StampedEvent::get_events(ctx.chain_store().blockstore(), events_root)
+        .context("load events amt")?;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad77eee and 35ac348.

📒 Files selected for processing (22)
  • src/benchmark_private/tipset_validation.rs (0 hunks)
  • src/chain/store/chain_store.rs (3 hunks)
  • src/chain_sync/chain_follower.rs (0 hunks)
  • src/daemon/context.rs (0 hunks)
  • src/daemon/db_util.rs (1 hunks)
  • src/db/car/many.rs (1 hunks)
  • src/db/memory.rs (1 hunks)
  • src/db/mod.rs (0 hunks)
  • src/db/parity_db.rs (1 hunks)
  • src/libp2p/chain_exchange/provider.rs (0 hunks)
  • src/rpc/methods/chain.rs (1 hunks)
  • src/rpc/methods/eth/filter/mod.rs (3 hunks)
  • src/rpc/methods/sync.rs (1 hunks)
  • src/state_manager/mod.rs (4 hunks)
  • src/state_manager/tests.rs (4 hunks)
  • src/tool/offline_server/server.rs (0 hunks)
  • src/tool/subcommands/api_cmd/api_compare_tests.rs (0 hunks)
  • src/tool/subcommands/api_cmd/generate_test_snapshot.rs (1 hunks)
  • src/tool/subcommands/api_cmd/test_snapshot.rs (2 hunks)
  • src/tool/subcommands/api_cmd/test_snapshots.txt (1 hunks)
  • src/tool/subcommands/index_cmd.rs (0 hunks)
  • src/tool/subcommands/state_compute_cmd.rs (0 hunks)
💤 Files with no reviewable changes (9)
  • src/tool/offline_server/server.rs
  • src/libp2p/chain_exchange/provider.rs
  • src/tool/subcommands/state_compute_cmd.rs
  • src/daemon/context.rs
  • src/db/mod.rs
  • src/tool/subcommands/index_cmd.rs
  • src/benchmark_private/tipset_validation.rs
  • src/tool/subcommands/api_cmd/api_compare_tests.rs
  • src/chain_sync/chain_follower.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/tool/subcommands/api_cmd/test_snapshots.txt
  • src/state_manager/tests.rs
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-10-17T14:24:47.046Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6167
File: src/tool/subcommands/state_compute_cmd.rs:89-91
Timestamp: 2025-10-17T14:24:47.046Z
Learning: In `src/tool/subcommands/state_compute_cmd.rs`, when using `ReadOpsTrackingStore` to generate minimal snapshots, `HEAD_KEY` should be written to `db.tracker` (not `db` itself) before calling `export_forest_car()`, because the export reads from the tracker MemoryDB which accumulates only the accessed data during computation.

Applied to files:

  • src/db/memory.rs
  • src/daemon/db_util.rs
  • src/tool/subcommands/api_cmd/generate_test_snapshot.rs
  • src/tool/subcommands/api_cmd/test_snapshot.rs
📚 Learning: 2025-09-11T16:03:14.328Z
Learnt from: akaladarshi
Repo: ChainSafe/forest PR: 6000
File: src/tool/subcommands/api_cmd/state_decode_params_tests/miner.rs:4-9
Timestamp: 2025-09-11T16:03:14.328Z
Learning: In the Forest codebase's state_decode_params_tests module, common imports like `to_vec`, `TokenAmount`, `Cid`, etc. are centralized in the mod.rs file and made available to submodules through `use super::*;`, eliminating the need for duplicate imports in individual test files.

Applied to files:

  • src/state_manager/mod.rs
  • src/db/car/many.rs
  • src/tool/subcommands/api_cmd/generate_test_snapshot.rs
📚 Learning: 2025-08-08T12:11:55.266Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 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/rpc/methods/chain.rs
  • src/daemon/db_util.rs
  • src/rpc/methods/eth/filter/mod.rs
  • src/tool/subcommands/api_cmd/test_snapshot.rs
📚 Learning: 2025-08-25T13:35:24.230Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5969
File: src/tool/subcommands/snapshot_cmd.rs:412-412
Timestamp: 2025-08-25T13:35:24.230Z
Learning: In src/tool/subcommands/snapshot_cmd.rs, the +1 in `last_epoch = ts.epoch() - epochs as i64 + 1` fixes an off-by-1 bug where specifying --check-stateroots=N would validate N+1 epochs instead of N epochs, causing out-of-bounds errors when the snapshot contains only N recent state roots.

Applied to files:

  • src/tool/subcommands/api_cmd/test_snapshot.rs
📚 Learning: 2025-08-11T14:00:47.060Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5886
File: src/daemon/db_util.rs:236-259
Timestamp: 2025-08-11T14:00:47.060Z
Learning: In Forest's snapshot import (`src/daemon/db_util.rs`), when F3 CID is present in snapshot metadata but the actual F3 data is missing, this should cause a hard error as it indicates snapshot corruption. Only errors during the F3 import process itself (after data is successfully retrieved) should be non-fatal and logged.

Applied to files:

  • src/tool/subcommands/api_cmd/test_snapshot.rs
🧬 Code graph analysis (8)
src/db/memory.rs (3)
src/chain/store/chain_store.rs (1)
  • db (503-503)
src/chain/store/index.rs (1)
  • db (48-50)
src/rpc/methods/state.rs (1)
  • db (1443-1444)
src/rpc/methods/chain.rs (3)
src/rpc/methods/eth/filter/mod.rs (1)
  • get_events_by_event_root (369-383)
src/tool/subcommands/api_cmd/generate_test_snapshot.rs (1)
  • ctx (99-151)
src/tool/subcommands/api_cmd/test_snapshot.rs (1)
  • ctx (122-169)
src/db/car/many.rs (3)
src/db/memory.rs (2)
  • SettingsStoreExt (32-32)
  • SettingsStoreExt (156-156)
src/db/parity_db.rs (1)
  • SettingsStoreExt (177-177)
src/chain/mod.rs (1)
  • SettingsStoreExt (45-45)
src/rpc/methods/sync.rs (1)
src/chain/store/chain_store.rs (4)
  • new (117-142)
  • new (567-571)
  • db (503-503)
  • chain_config (247-249)
src/tool/subcommands/api_cmd/generate_test_snapshot.rs (5)
src/db/car/many.rs (1)
  • SettingsStoreExt (256-256)
src/db/memory.rs (2)
  • SettingsStoreExt (32-32)
  • SettingsStoreExt (156-156)
src/db/parity_db.rs (1)
  • SettingsStoreExt (177-177)
src/chain/mod.rs (1)
  • SettingsStoreExt (45-45)
src/db/mod.rs (1)
  • open_db (283-285)
src/chain/store/chain_store.rs (3)
src/chain/store/index.rs (2)
  • db (48-50)
  • new (42-46)
src/state_manager/mod.rs (2)
  • new (181-183)
  • chain_config (359-361)
src/daemon/context.rs (1)
  • chain_config (65-67)
src/rpc/methods/eth/filter/mod.rs (1)
src/shim/executor.rs (3)
  • events (118-124)
  • events_root (180-186)
  • get_events (392-414)
src/tool/subcommands/api_cmd/test_snapshot.rs (1)
src/chain/store/chain_store.rs (6)
  • db (503-503)
  • eth_mappings (180-182)
  • eth_mappings (196-198)
  • new (117-142)
  • new (567-571)
  • chain_config (247-249)
⏰ 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). (3)
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: tests-release
  • GitHub Check: tests
🔇 Additional comments (25)
src/db/car/many.rs (1)

14-15: LGTM - Import cleanup aligns with IndicesStore removal.

The consolidated import statement correctly removes IndicesStore while retaining all other necessary traits for ManyCar functionality.

src/db/memory.rs (1)

6-6: LGTM - Clean removal of IndicesStore import.

The import change correctly reflects the removal of the IndicesStore trait implementation from MemoryDB.

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

9-9: LGTM - Import cleanup consistent with IndicesStore removal.

The import correctly removes IndicesStore and IndicesStoreExt while retaining EthMappingsStore and EthMappingsStoreExt which are still used for Ethereum mapping functionality.


743-744: Test correctly updated for new ChainStore::new signature.

The test now passes 5 arguments matching the updated constructor: (db, heaviest_tipset_key_provider, eth_mappings, chain_config, genesis_block_header).


758-758: LGTM - Consistent with other test updates.

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

189-191: LGTM - Test correctly updated for new ChainStore::new signature.

The ctx() function now constructs ChainStore with the updated 5-argument signature, consistent with the removal of the IndicesStore parameter.

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

55-64: Simplified backfill logic using let-chains.

The refactored function cleanly chains the conditionals. Note that try_write() will silently skip the backfill if the lock is already held elsewhere. This is likely acceptable for test scenarios but worth being aware of.


134-140: Improved error handling with ? operator.

Good change - using ? instead of unwrap() properly propagates errors through the anyhow::Result return type.

src/daemon/db_util.rs (1)

332-334: Event root indexing removed from tipset processing.

The compute_tipset_state result is no longer unpacked to extract and index events_roots. This aligns with the PR objective of serving events directly from the blockstore rather than maintaining a separate index. Events are now stored as part of state computation and retrieved via StampedEvent::get_events when needed.

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

254-256: Simplified event retrieval aligned with Lotus behavior.

The handler now directly fetches events from the blockstore via EthEventHandler::get_events_by_event_root. This removes the previous tipset-resolution flow and duplicate-event logic that caused issue #6271. If events aren't present in the blockstore, the call will error out with an AMT load error, matching Lotus's behavior.


1610-1618: Test helper correctly updated for new ChainStore signature.

The _load helper now constructs ChainStore with the updated 5-argument signature, using separate MemoryDB instances for heaviest_tipset_key_provider and eth_mappings.

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

13-14: LGTM on import updates.

The imports are correctly updated to remove IndicesStore while retaining the necessary EthMappingsStore, SettingsStore, and related types for the tracking store functionality.


83-97: LGTM on simplified build_index function.

The function now only builds the eth_mappings index from the tracker, which aligns with the removal of the indices store column. The logic correctly returns None when no mappings are present.


112-121: Verify ChainStore::new signature consistency.

The constructor takes 5 parameters: db (Arc), heaviest_tipset_key_provider (Arc<dyn HeaviestTipsetKeyProvider + Sync + Send>), eth_mappings (Arc<dyn EthMappingsStore + Sync + Send>), chain_config, and genesis_block_header. All call sites consistently pass the same db instance for the first three parameters, which is valid since the concrete db type implements all required traits.

src/db/parity_db.rs (3)

4-4: LGTM on import simplification.

The IndicesStore import is correctly removed, leaving only the necessary store traits.


23-42: LGTM on DbColumn enum update.

The Indices variant is cleanly removed. The remaining columns (GraphDagCborBlake2b256, GraphFull, Settings, EthMappings, PersistentGraph) are properly ordered and documented.


44-81: LGTM on create_column_options update.

The match arms correctly handle all remaining column variants with appropriate options (preimage, btree_index, compression settings).

src/state_manager/mod.rs (6)

64-64: LGTM on import update.

Adding ensure from anyhow is appropriate for the new validation logic in step 6.


99-105: LGTM on StateEvents struct.

The struct appropriately holds paired events and roots vectors with proper derives for debugging, cloning, and memory tracking.


550-576: LGTM on tipset_state_events fallback logic.

The method now properly falls back to computing the tipset state when events are not found in cache, returning a StateEvents struct with both events and roots.


1992-2003: Good defensive check for AMT root consistency.

The ensure! macro correctly validates that the derived event root matches the FVM-computed root. This is essential for detecting any discrepancies in event serialization or AMT construction.


94-94: EVENTS_AMT_BITWIDTH value is verified against FVM at runtime.

The bitwidth of 5 is already validated through the equality check at lines 2000-2001, where the derived event root is compared against the FVM-computed root. If this constant were misaligned with FVM's event AMT construction, the ensure!() check would fail. No additional verification needed.


71-71: Confirm the intentionality of using Amtv0 for receipt roots and Amt for event roots.

The code uses Amtv0::new_from_iter for receipt construction and Amt::new_from_iter_with_bit_width for events. While Amtv0 is documented as the legacy AMT encoding (for backward compatibility) and Amt is the current encoding, the codebase lacks explicit documentation explaining why receipts specifically require Amtv0. Consider adding a comment referencing the Lotus behavior or actor specification that mandates this version distinction.

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

40-40: LGTM on StampedEvent import.

The import is correctly added to support the new get_events_by_event_root implementation that loads events directly via StampedEvent::get_events.


283-290: LGTM on improved error messages.

The error message now includes the concrete lengths for better debugging when there's a mismatch between messages and events.

@LesnyRumcajs
Copy link
Member

DB migration check in CI is failing because I've removed the Indices column from DB so need to create a new migration or maybe go back to old not sure right now.

I was thinking, if I can get some comments on the PR, to finalise before the migration, it would be helpful 🙏 @hanabi1224 @LesnyRumcajs

Before moving forward without the indices column, I'd like to first see some numbers showing the performance impact for affected methods.

match StampedEvent::get_events(ctx.chain_store().blockstore(), events_root) {
Ok(e) => e,
Err(e) => {
return Err(anyhow::anyhow!("load events amt: {}", e));
Copy link
Member

Choose a reason for hiding this comment

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

nit

Suggested change
return Err(anyhow::anyhow!("load events amt: {}", e));
bail!("load events amt: {e}");

Copy link
Member

Choose a reason for hiding this comment

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

Actually, probably let state_events = bla.context("failed getting events")?; would make more sense.

Copy link
Collaborator Author

@akaladarshi akaladarshi Dec 10, 2025

Choose a reason for hiding this comment

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

The current error doesn't make much sense but I was trying to match lotus API level error , hence this

) -> anyhow::Result<Vec<StampedEvent>> {
let mut events = Vec::new();

// Try StampedEvent_v4 first (StampedEvent_v4 and StampedEvent_v3 are identical, use v4 here)
Copy link
Member

Choose a reason for hiding this comment

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

are there other versions as well?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, for events there is only two versions , V3 and V4,

pub enum StampedEvent {


// Try StampedEvent_v4 first (StampedEvent_v4 and StampedEvent_v3 are identical, use v4 here)
if let Ok(amt) = Amt::<StampedEvent_v4, _>::load(events_root, db) {
amt.for_each(|_, event| {
Copy link
Member

Choose a reason for hiding this comment

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

for_each_cacheless is a better option yielding faster iteration if the AMT is not iterated multiple times.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

// step 5: construct receipt root from receipts and flush the state-tree
let receipt_root = Amt::new_from_iter(chain_index.db(), receipts)?;
// step 5: construct receipt root from receipts
let receipt_root = Amtv0::new_from_iter(chain_index.db(), receipts)?;
Copy link
Member

Choose a reason for hiding this comment

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

why v0?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Earlier it was V0 only, but we renamed it to Amt .
It was being used like this

use fil_actors_shared::fvm_ipld_amt::Amtv0 as Amt;

// step 5: construct receipt root from receipts
let receipt_root = Amtv0::new_from_iter(chain_index.db(), receipts)?;

// step 6: store events AMTs in the blockstore
Copy link
Member

Choose a reason for hiding this comment

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

is there corresponding Lotus logic?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah , lotus has similar logic here

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

🧹 Nitpick comments (1)
src/shim/executor.rs (1)

390-414: get_events implementation matches goals (v4‑first, v3 fallback, ordered, no dedup)

The new StampedEvent::get_events correctly:

  • Loads from the events AMT root via Amt<StampedEvent_v4> first, then cleanly falls back to Amt<StampedEvent_v3> on error.
  • Uses for_each_cacheless, which is appropriate for single‑pass iteration and preserves AMT index order, so events are returned in the stored order with duplicates intact (aligning with the new ChainGetEvents behavior).

If you expect very large event sets, you could optionally pre‑allocate events with events.reserve(amt.count() as usize) before iteration for a minor perf win, but that’s not required for correctness.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35ac348 and 1dd279f.

📒 Files selected for processing (1)
  • src/shim/executor.rs (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-17T15:21:40.753Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5841
File: src/utils/get_size/mod.rs:10-10
Timestamp: 2025-07-17T15:21:40.753Z
Learning: The get_size2 crate's GetSize trait provides default implementations: get_stack_size() uses std::mem::size_of, get_heap_size() returns 0, and get_size() returns their sum. An empty impl like `impl GetSize for MyType {}` is valid and uses these defaults, making it suitable for simple wrapper types that don't allocate heap memory.

Applied to files:

  • src/shim/executor.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: tests-release
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: tests
  • GitHub Check: All lint checks
  • GitHub Check: Build Ubuntu
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: Build MacOS
🔇 Additional comments (2)
src/shim/executor.rs (2)

10-10: Amt import aligns with new events AMT usage

Pulling in Amt alongside Amtv0 is consistent with using the new AMT API for events while keeping receipts on Amtv0. Please just double‑check that the version of Amt here matches how events are actually stored and how Lotus reads them, so ChainGetEvents parity is guaranteed end‑to‑end.


340-346: Untagged StampedEvent serialization looks appropriate for RPC parity

Adding #[serde(untagged)] to StampedEvent matches how Receipt is handled and should make the serialized shape match the underlying FVM event types (and Lotus) instead of introducing an enum wrapper. Since we only derive Serialize, this shouldn’t introduce deserialization ambiguity. Please confirm that no external clients relied on the previously tagged JSON shape of StampedEvent (if it was ever exposed directly).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RPC requires calibnet RPC checks to run on CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Faulty deduplication Filecoin.ChainGetEvents

3 participants