Skip to content

Conversation

@LesnyRumcajs
Copy link
Member

@LesnyRumcajs LesnyRumcajs commented Jan 9, 2026

Summary of changes

Changes introduced in this pull request:

  • changed the data structure returned by the newHeads response to Ethereum block (it was an array of tipset headers (???)). filecoin-subway doesn't complain anymore.

Reference issue to close (if applicable)

Closes #6400

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

  • Bug Fixes

    • Subscription for newHeads now delivers Ethereum-compatible block payloads and continues streaming if individual conversions fail.
  • New Features

    • Added option to receive full transaction objects or only transaction hashes in block responses.
  • Compatibility

    • Subscription payload shape updated to reflect Ethereum block format.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 9, 2026

Walkthrough

Replace Filecoin tipset headers with Ethereum-style Block objects for eth_subscribe newHeads; add Block::from_filecoin_tipset(ctx, tipset, TxInfo) and TxInfo enum; change chain::new_heads signature to take owned Ctx<DB>; update ApiHeaders to contain Block.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Added unreleased entry noting fix: eth_subscribe newHeads now reports Ethereum block format.
Chain RPC Handler
src/rpc/methods/chain.rs
new_heads signature updated to accept Ctx<DB> (owned context); converts tipsets to Eth Block via Block::from_filecoin_tipset(..., TxInfo::Full) on Apply events; logs and continues on conversion errors.
Ethereum Block Conversion
src/rpc/methods/eth.rs
Added pub enum TxInfo { Hash, Full }, impl From<bool> for TxInfo, and pub async fn Block::from_filecoin_tipset<DB>(ctx, tipset, tx_info) -> Result<Block>; removed block_from_filecoin_tipset function and updated callers to the new API.
PubSub Handler
src/rpc/methods/eth/pubsub.rs
Callsite updated to pass owned ctx into chain::new_heads (reflecting new signature).
Types
src/rpc/methods/eth/types.rs
ApiHeaders inner type changed from Vec<CachingBlockHeader> to Block (serialization via existing lotus_json).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant PubSub as PubSub Handler
    participant Chain as chain::new_heads
    participant Conv as Block::from_filecoin_tipset
    participant DB as Blockstore
    participant Sender as Stream Sender

    Client->>PubSub: eth_subscribe("newHeads")
    PubSub->>Chain: chain::new_heads(ctx)
    Chain-->>PubSub: (Subscriber, JoinHandle)
    PubSub-->>Client: subscription_id

    Note over Chain: On tipset Apply events
    Chain->>Conv: from_filecoin_tipset(ctx.clone(), tipset, TxInfo::Full)
    Conv->>DB: fetch state/txs
    DB-->>Conv: state/tx data
    Conv-->>Chain: Eth Block or error

    alt Conversion Success
        Chain->>Sender: send ApiHeaders(Block)
        Sender-->>Client: eth_subscription notification
    else Conversion Error
        Chain->>Chain: log error and continue
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PR #5749 — Overlaps changes to chain::new_heads and ApiHeaders emission to Ethereum blocks.
  • PR #6404 — Related changes to block-from-tipset API and callers; interacts with new Block::from_filecoin_tipset usage.

Suggested labels

RPC

Suggested reviewers

  • hanabi1224
  • sudo-shashank
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 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 main change: replacing the data structure returned by eth_subscribe newHeads from Filecoin tipset headers to Ethereum-style block format.
Linked Issues check ✅ Passed The code changes implement the objectives from issue #6400: converting eth_subscribe newHeads to return EthBlock instead of Filecoin tipset headers, with the new Block::from_filecoin_tipset method enabling proper Ethereum block format conversion.
Out of Scope Changes check ✅ Passed All code changes align with the stated objective to fix eth_subscribe newHeads format; the refactoring of public APIs and introduction of TxInfo are necessary to support this core change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

@LesnyRumcajs LesnyRumcajs force-pushed the fix-eth-subscribe-heads branch from d6912e8 to 6eabb3d Compare January 13, 2026 16:31
@LesnyRumcajs LesnyRumcajs marked this pull request as ready for review January 13, 2026 17:11
@LesnyRumcajs LesnyRumcajs requested a review from a team as a code owner January 13, 2026 17:11
@LesnyRumcajs LesnyRumcajs requested review from hanabi1224 and sudo-shashank and removed request for a team January 13, 2026 17:11
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/rpc/methods/eth.rs (2)

557-630: Consider extracting shared transaction processing logic.

This new method duplicates significant logic from block_from_filecoin_tipset (lines 1601-1627), particularly the message iteration and ApiEthTx construction. While the non-caching behavior is intentional for the eth_subscribe use case, consider extracting the common transaction-building logic into a helper function to reduce duplication.

♻️ Suggested helper extraction
// Helper to build transactions from messages and receipts
fn build_eth_transactions<DB: Blockstore>(
    msgs_and_receipts: &[(ChainMessage, Receipt)],
    state_tree: &StateTree<DB>,
    eth_chain_id: EthChainIdType,
    block_hash: &EthHash,
    block_number: &EthUint64,
) -> Result<Vec<ApiEthTx>> {
    let mut transactions = Vec::new();
    for (i, (msg, receipt)) in msgs_and_receipts.iter().enumerate() {
        let ti = EthUint64(i as u64);
        let smsg = match msg {
            ChainMessage::Signed(msg) => msg.clone(),
            ChainMessage::Unsigned(msg) => {
                let sig = Signature::new_bls(vec![]);
                SignedMessage::new_unchecked(msg.clone(), sig)
            }
        };
        let mut tx = new_eth_tx_from_signed_message(&smsg, state_tree, eth_chain_id)?;
        tx.block_hash = block_hash.clone();
        tx.block_number = block_number.clone();
        tx.transaction_index = ti;
        transactions.push(tx);
    }
    Ok(transactions)
}

1678-1683: Consider implementing From<bool> for TxInfo to reduce repetition.

The bool-to-enum conversion pattern is repeated in three handlers. A From implementation would make this more concise.

♻️ Optional: Add From implementation
impl From<bool> for TxInfo {
    fn from(full: bool) -> Self {
        if full { TxInfo::Full } else { TxInfo::Hash }
    }
}

Then call sites become:

let block = block_from_filecoin_tipset(ctx, ts, full_tx_info.into()).await?;
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c87fa1 and cce1f2e.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/pubsub.rs
  • src/rpc/methods/eth/types.rs
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: LesnyRumcajs
Repo: ChainSafe/forest PR: 5907
File: src/rpc/methods/state.rs:523-570
Timestamp: 2025-08-06T15:44:33.467Z
Learning: LesnyRumcajs prefers to rely on BufWriter's Drop implementation for automatic flushing rather than explicit flush() calls in Forest codebase.
📚 Learning: 2026-01-05T12:54:40.850Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6381
File: src/lotus_json/actors/states/cron_state.rs:8-8
Timestamp: 2026-01-05T12:54:40.850Z
Learning: In Rust code reviews, do not derive Eq for a struct if any field does not implement Eq (e.g., types from external dependencies). If a type like CronStateLotusJson includes fields wrapping external dependencies that lack Eq, derive PartialEq (or implement PartialEq manually) but avoid deriving Eq. This ensures comparisons compile and reflect actual equivalence semantics. When needed, consider implementing custom PartialEq (and possibly Eq) only after ensuring all fields (or wrappers) implement Eq, or keep PartialEq-only if full equality semantics cannot be expressed.

Applied to files:

  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/eth/pubsub.rs
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
📚 Learning: 2026-01-05T12:56:13.802Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6381
File: src/lotus_json/actors/states/evm_state.rs:41-44
Timestamp: 2026-01-05T12:56:13.802Z
Learning: In Rust codebases (e.g., Forest), do not add #[cfg(test)] to functions already annotated with #[test]. The #[test] attribute ensures the function is compiled only for tests, so a separate #[cfg(test)] is redundant and can be removed if present. Apply this check to all Rust files that contain #[test] functions.

Applied to files:

  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/eth/pubsub.rs
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
📚 Learning: 2025-10-16T11:05:13.586Z
Learnt from: elmattic
Repo: ChainSafe/forest PR: 6166
File: src/auth/mod.rs:127-150
Timestamp: 2025-10-16T11:05:13.586Z
Learning: In Rust 2024, `std::env::set_var` and `std::env::remove_var` are unsafe functions. They require `unsafe` blocks because they are only safe in single-threaded programs (Windows is an exception). When these functions are used in tests, ensure proper serialization with `#[serial]` or similar mechanisms.

Applied to files:

  • src/rpc/methods/chain.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 (3)
src/rpc/methods/eth/pubsub.rs (1)
src/rpc/methods/chain.rs (19)
  • handle (173-197)
  • handle (240-255)
  • handle (269-284)
  • handle (299-313)
  • handle (328-360)
  • handle (373-381)
  • handle (394-405)
  • handle (418-530)
  • handle (543-567)
  • handle (580-587)
  • handle (600-643)
  • handle (656-680)
  • handle (696-705)
  • handle (720-725)
  • handle (740-778)
  • handle (793-809)
  • chain (463-463)
  • chain (501-501)
  • new_heads (81-113)
src/rpc/methods/chain.rs (1)
src/rpc/methods/eth.rs (5)
  • eth_logs_with_filter (1537-1561)
  • from_filecoin_tipset (561-630)
  • new (544-556)
  • new (783-788)
  • e (2069-2069)
src/rpc/methods/eth.rs (3)
src/shim/state_tree.rs (1)
  • new_from_root (168-188)
src/shim/crypto.rs (1)
  • new_bls (78-83)
src/message/signed_message.rs (1)
  • new_unchecked (35-37)
⏰ 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). (8)
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: Build MacOS
  • GitHub Check: Build Ubuntu
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: All lint checks
  • GitHub Check: tests-release
  • GitHub Check: Coverage
  • GitHub Check: rubocop
🔇 Additional comments (9)
CHANGELOG.md (1)

59-60: Changelog entry looks accurate and appropriately scoped.

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

424-428: Confirm schema/doc generation doesn’t require JsonSchema for subscription payload types. If subscriptions are included in OpenRPC/schemars output, ApiHeaders may need #[derive(JsonSchema)] (and any necessary schemars(...) attrs) to avoid doc/test breakage.

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

121-130: Call-site update matches the new chain::new_heads(Ctx<DB>) signature.

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

81-113: Double-check TxInfo::Full for newHeads (payload size + performance). If consumers only expect header-ish fields or tx hashes, consider emitting TxInfo::Hash instead to reduce per-head execution/serialization cost.

Proposed adjustment (if compatible with Lotus/tooling expectations)
-                    match EthBlock::from_filecoin_tipset(data.clone(), Arc::new(ts), TxInfo::Full)
+                    match EthBlock::from_filecoin_tipset(data.clone(), Arc::new(ts), TxInfo::Hash)
                         .await
                     {
src/rpc/methods/eth.rs (5)

534-541: LGTM!

Clean enum definition with appropriate derives and clear documentation. The Copy trait is correctly derived for this simple two-variant enum.


1578-1582: LGTM!

Good refactor from boolean to enum parameter - improves code readability at call sites.


1648-1653: LGTM!

Correct transformation from boolean condition to enum comparison.


1708-1713: LGTM!

Consistent with the conversion pattern used in EthGetBlockByHash.


1735-1740: LGTM!

Consistent implementation across all V2 handlers.

@codecov
Copy link

codecov bot commented Jan 13, 2026

Codecov Report

❌ Patch coverage is 76.19048% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.52%. Comparing base (d29781d) to head (e58d247).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/rpc/methods/eth.rs 85.33% 5 Missing and 6 partials ⚠️
src/rpc/methods/chain.rs 0.00% 8 Missing ⚠️
src/rpc/methods/eth/pubsub.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
Files with missing lines Coverage Δ
src/rpc/methods/eth/types.rs 65.03% <ø> (ø)
src/rpc/methods/eth/pubsub.rs 0.00% <0.00%> (ø)
src/rpc/methods/chain.rs 52.65% <0.00%> (-0.30%) ⬇️
src/rpc/methods/eth.rs 67.66% <85.33%> (+0.20%) ⬆️

... and 9 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d29781d...e58d247. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@LesnyRumcajs LesnyRumcajs marked this pull request as draft January 13, 2026 18:20
@LesnyRumcajs LesnyRumcajs force-pushed the fix-eth-subscribe-heads branch from cc35ba3 to 220ec49 Compare January 14, 2026 09:53
@LesnyRumcajs LesnyRumcajs marked this pull request as ready for review January 14, 2026 10:50
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 (1)
src/rpc/methods/chain.rs (1)

80-110: Handle broadcast lag explicitly in the subscription loop.

The pattern while let Ok(v) = subscriber.recv().await silently exits when the broadcast channel returns RecvError::Lagged, which occurs when this task falls behind the event queue. Since each message now requires an awaited block conversion, lag is more likely under load. Other subscribers in the codebase (state_manager, message_pool, eth/pubsub, rpc/channel) explicitly handle RecvError::Lagged and continue; this function should do the same.

Proposed fix
 pub(crate) fn new_heads<DB: Blockstore + Send + Sync + 'static>(
     data: Ctx<DB>,
 ) -> (Subscriber<ApiHeaders>, JoinHandle<()>) {
     let (sender, receiver) = broadcast::channel(HEAD_CHANNEL_CAPACITY);

     let mut subscriber = data.chain_store().publisher().subscribe();

     let handle = tokio::spawn(async move {
-        while let Ok(v) = subscriber.recv().await {
+        loop {
+            let v = match subscriber.recv().await {
+                Ok(v) => v,
+                Err(tokio::sync::broadcast::error::RecvError::Lagged(n)) => {
+                    tracing::warn!("new_heads lagged; skipped {n} head-change events");
+                    continue;
+                }
+                Err(tokio::sync::broadcast::error::RecvError::Closed) => break,
+            };
             let headers = match v {
                 HeadChange::Apply(ts) => {
                     // Convert the tipset to an Ethereum block with full transaction info
                     // Note: In Filecoin's Eth RPC, a tipset maps to a single Ethereum block
                     match EthBlock::from_filecoin_tipset(data.clone(), ts, TxInfo::Full).await {
                         Ok(block) => ApiHeaders(block),
                         Err(e) => {
                             tracing::error!("Failed to convert tipset to eth block: {}", e);
                             continue;
                         }
                     }
                 }
             };
             if let Err(e) = sender.send(headers) {
                 tracing::error!("Failed to send headers: {}", e);
                 break;
             }
         }
     });

     (receiver, handle)
 }
🧹 Nitpick comments (2)
src/rpc/methods/chain.rs (1)

90-100: Reconsider TxInfo::Full for newHeads (payload size + execution cost).
Using TxInfo::Full here likely forces tipset execution and builds full transaction objects for every head update. If consumers only need a “header-like” newHeads (common in Ethereum tooling), prefer TxInfo::Hash (or even a header-only type) and let clients call eth_getBlockByHash when they need full txs.

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

564-645: Avoid building full transactions when TxInfo::Hash is requested (especially on cache miss).
Right now, TxInfo::Hash still constructs Transactions::Full (including state-tree lookups) and only then downgrades to hashes. If eth_getBlockBy* is called with fullTxInfo=false, this is avoidable overhead. Consider branching earlier and building only hashes (or caching a lightweight representation) when TxInfo::Hash.

Sketch of the refactor direction
-            let mut full_transactions = vec![];
+            let mut full_transactions = vec![];
+            let mut tx_hashes = vec![];

             let mut gas_used = 0;
             for (i, (msg, receipt)) in msgs_and_receipts.iter().enumerate() {
                 let ti = EthUint64(i as u64);
                 gas_used += receipt.gas_used();
                 let smsg = match msg {
                     ChainMessage::Signed(msg) => msg.clone(),
                     ChainMessage::Unsigned(msg) => {
                         let sig = Signature::new_bls(vec![]);
                         SignedMessage::new_unchecked(msg.clone(), sig)
                     }
                 };

-                let mut tx = new_eth_tx_from_signed_message(
-                    &smsg,
-                    &state_tree,
-                    ctx.chain_config().eth_chain_id,
-                )?;
-                tx.block_hash = block_hash.clone();
-                tx.block_number = block_number.clone();
-                tx.transaction_index = ti;
-                full_transactions.push(tx);
+                if tx_info == TxInfo::Full {
+                    let mut tx = new_eth_tx_from_signed_message(
+                        &smsg,
+                        &state_tree,
+                        ctx.chain_config().eth_chain_id,
+                    )?;
+                    tx.block_hash = block_hash.clone();
+                    tx.block_number = block_number.clone();
+                    tx.transaction_index = ti;
+                    full_transactions.push(tx);
+                } else {
+                    // Compute just the hash (delegated eth tx hash vs CID-based for native msgs),
+                    // ideally via a shared helper so it matches other endpoints.
+                    tx_hashes.push(/* ... */);
+                }
             }

             let b = Block {
                 ...
-                transactions: Transactions::Full(full_transactions),
+                transactions: if tx_info == TxInfo::Full {
+                    Transactions::Full(full_transactions)
+                } else {
+                    Transactions::Hash(tx_hashes)
+                },
                 ...
             };
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cce1f2e and 220ec49.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/pubsub.rs
  • src/rpc/methods/eth/types.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/rpc/methods/eth/pubsub.rs
  • CHANGELOG.md
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Use anyhow::Result<T> for most operations and add context with .context() when operations fail
Use tokio::task::spawn_blocking for CPU-intensive work such as VM execution and cryptography operations
Use .get() instead of indexing with [index] for safe element access
Do not use unwrap() in production code; use ? or expect() with descriptive messages instead
Do not use dbg! macro in non-test Rust code
Do not use todo! macro in non-test Rust code
Use strum crate for enum string conversions
Use derive_more crate for common trait implementations
Document all public functions and structs with doc comments
Use #[cfg(test)] or #[cfg(feature = "doctest-private")] for test-only code
Use channel-based communication (flume, tokio channels) between async tasks
Use consistent formatting following cargo fmt standards
Ensure code passes cargo clippy --all-targets --no-deps -- --deny=warnings linter checks

Files:

  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/eth.rs
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: ChainSafe/forest PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-14T10:25:49.996Z
Learning: Publisher/Subscriber Pattern: Broadcast `HeadChange` events to multiple subscribers (RPC, message pool, chain indexer)
Learnt from: LesnyRumcajs
Repo: ChainSafe/forest PR: 5907
File: src/rpc/methods/state.rs:523-570
Timestamp: 2025-08-06T15:44:33.467Z
Learning: LesnyRumcajs prefers to rely on BufWriter's Drop implementation for automatic flushing rather than explicit flush() calls in Forest codebase.
📚 Learning: 2026-01-14T10:25:49.996Z
Learnt from: CR
Repo: ChainSafe/forest PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-14T10:25:49.996Z
Learning: Publisher/Subscriber Pattern: Broadcast `HeadChange` events to multiple subscribers (RPC, message pool, chain indexer)

Applied to files:

  • src/rpc/methods/chain.rs
📚 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/rpc/methods/chain.rs
📚 Learning: 2026-01-05T12:54:40.850Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6381
File: src/lotus_json/actors/states/cron_state.rs:8-8
Timestamp: 2026-01-05T12:54:40.850Z
Learning: In Rust code reviews, do not derive Eq for a struct if any field does not implement Eq (e.g., types from external dependencies). If a type like CronStateLotusJson includes fields wrapping external dependencies that lack Eq, derive PartialEq (or implement PartialEq manually) but avoid deriving Eq. This ensures comparisons compile and reflect actual equivalence semantics. When needed, consider implementing custom PartialEq (and possibly Eq) only after ensuring all fields (or wrappers) implement Eq, or keep PartialEq-only if full equality semantics cannot be expressed.

Applied to files:

  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/eth.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: 2026-01-05T12:56:13.802Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6381
File: src/lotus_json/actors/states/evm_state.rs:41-44
Timestamp: 2026-01-05T12:56:13.802Z
Learning: In Rust codebases (e.g., Forest), do not add #[cfg(test)] to functions already annotated with #[test]. The #[test] attribute ensures the function is compiled only for tests, so a separate #[cfg(test)] is redundant and can be removed if present. Apply this check to all Rust files that contain #[test] functions.

Applied to files:

  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/eth.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). (3)
  • GitHub Check: Coverage
  • GitHub Check: tests-release
  • GitHub Check: Build forest binaries on Linux AMD64
🔇 Additional comments (4)
src/rpc/methods/eth/types.rs (1)

424-428: Confirm ApiHeaders serialization matches “newHeads” expectations (object shape, no accidental wrapping).
ApiHeaders now wraps Block, which is the intended API change, but the #[serde(with = "crate::lotus_json")] on the tuple field makes it easy to accidentally change the emitted JSON shape (especially if lotus_json uses a different representation than standard serde for Block). Please sanity-check with an actual WS eth_subscribe("newHeads") payload that result is a single Ethereum-style object (and that field casing/hex formatting matches Lotus/tooling expectations).

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

534-547: TxInfo looks fine; keep the enum as the public “detail knob”.
This makes call sites clearer than threading a boolean and leaves room for future variants (e.g., HeaderOnly).


590-632: Spec-sensitive: please confirm ordering + state used for address resolution matches Lotus.
from_filecoin_tipset uses messages_for_tipset order and resolves addresses against a single StateTree built from the post-tipset state_root. If Lotus uses different ordering (BLS/secp grouping) or a different state view for address lookups, hashes/tx objects could diverge from expected tooling behavior.


1608-1666: Call sites updated cleanly to the new API.
Switching full_tx_info: bool to TxInfo keeps the RpcMethod signatures stable while letting Block::from_filecoin_tipset own the behavior.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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 (2)
src/rpc/methods/chain.rs (1)

80-110: Potential perf/compat risk: newHeads hardcodes full transactions (TxInfo::Full), while on-demand RPC methods let clients choose.

The subscription always calls EthBlock::from_filecoin_tipset(..., TxInfo::Full) (line 93), building full transaction objects on every head notification. Meanwhile, eth_getBlockByNumber and eth_getBlockByHash take a boolean parameter to let clients choose between TxInfo::Full and TxInfo::Hash.

Standard Ethereum eth_subscribe("newHeads") typically returns lightweight block headers without full transaction objects. The hardcoded Full variant likely incurs unnecessary VM execution and serialization overhead per block.

Recommendation: Either (1) make transaction detail configurable for newHeads subscriptions to match RPC method behavior, or (2) default to TxInfo::Hash and document why Full is necessary if there's a compatibility reason specific to Filecoin/Lotus.

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

121-130: By-value ctx is correct, but add lag logging for observability.

The switch to chain::new_heads(ctx) (line 126) matches the new signature and is appropriate. However, handle_subscription silently ignores RecvError::Lagged(_) (lines 174-175), which drops newHeads notifications without visibility into the issue. Given that EthBlock::from_filecoin_tipset is expensive and HEAD_CHANNEL_CAPACITY is small (10), lag is plausible.

Other parts of the codebase log similar lag events (e.g., message pool, state manager). Add at least a warning log when lag occurs to make this diagnosable:

Suggested fix
-                    Err(RecvError::Lagged(_)) => {
-                    }
+                    Err(RecvError::Lagged(n)) => {
+                        tracing::warn!("Subscription lagged; skipped {n} messages");
+                        continue;
+                    }
🤖 Fix all issues with AI agents
In `@src/rpc/methods/eth/types.rs`:
- Around line 424-427: Add a doc comment to the public newtype struct ApiHeaders
explaining its purpose and serialization behavior; specifically, above the
declaration of ApiHeaders (the #[derive(...)] pub struct ApiHeaders(#[serde(with
= "crate::lotus_json")] pub Block);) add a brief docstring describing that this
newtype wrapper emits a single Ethereum-style block object via the lotus_json
serializer (and mention the lotus_json_with_self! macro if helpful) to satisfy
the coding guideline for public structs.
🧹 Nitpick comments (1)
src/rpc/methods/eth.rs (1)

564-645: Consider avoiding “build full txs then downconvert” when tx_info == TxInfo::Hash.

Right now from_filecoin_tipset always builds Transactions::Full(...) (Lines 594-616, 630-631) and only at the end converts to hashes (Lines 637-642). If TxInfo::Hash becomes common (e.g., for newHeads), this will be unnecessary CPU/memory churn.

A follow-up optimization could build hashes directly (and optionally use a cache keyed by (tipset_cid, TxInfo) if you want to cache both forms).

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 220ec49 and 8d05dcf.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/pubsub.rs
  • src/rpc/methods/eth/types.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Use anyhow::Result<T> for most operations and add context with .context() when operations fail
Use tokio::task::spawn_blocking for CPU-intensive work such as VM execution and cryptography operations
Use .get() instead of indexing with [index] for safe element access
Do not use unwrap() in production code; use ? or expect() with descriptive messages instead
Do not use dbg! macro in non-test Rust code
Do not use todo! macro in non-test Rust code
Use strum crate for enum string conversions
Use derive_more crate for common trait implementations
Document all public functions and structs with doc comments
Use #[cfg(test)] or #[cfg(feature = "doctest-private")] for test-only code
Use channel-based communication (flume, tokio channels) between async tasks
Use consistent formatting following cargo fmt standards
Ensure code passes cargo clippy --all-targets --no-deps -- --deny=warnings linter checks

Files:

  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/pubsub.rs
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: ChainSafe/forest PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-14T10:25:49.996Z
Learning: Publisher/Subscriber Pattern: Broadcast `HeadChange` events to multiple subscribers (RPC, message pool, chain indexer)
Learnt from: LesnyRumcajs
Repo: ChainSafe/forest PR: 5907
File: src/rpc/methods/state.rs:523-570
Timestamp: 2025-08-06T15:44:33.467Z
Learning: LesnyRumcajs prefers to rely on BufWriter's Drop implementation for automatic flushing rather than explicit flush() calls in Forest codebase.
📚 Learning: 2026-01-14T10:25:49.996Z
Learnt from: CR
Repo: ChainSafe/forest PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-14T10:25:49.996Z
Learning: Publisher/Subscriber Pattern: Broadcast `HeadChange` events to multiple subscribers (RPC, message pool, chain indexer)

Applied to files:

  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth/pubsub.rs
📚 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/rpc/methods/chain.rs
  • src/rpc/methods/eth/pubsub.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: 2026-01-05T12:54:40.850Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6381
File: src/lotus_json/actors/states/cron_state.rs:8-8
Timestamp: 2026-01-05T12:54:40.850Z
Learning: In Rust code reviews, do not derive Eq for a struct if any field does not implement Eq (e.g., types from external dependencies). If a type like CronStateLotusJson includes fields wrapping external dependencies that lack Eq, derive PartialEq (or implement PartialEq manually) but avoid deriving Eq. This ensures comparisons compile and reflect actual equivalence semantics. When needed, consider implementing custom PartialEq (and possibly Eq) only after ensuring all fields (or wrappers) implement Eq, or keep PartialEq-only if full equality semantics cannot be expressed.

Applied to files:

  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/pubsub.rs
📚 Learning: 2026-01-05T12:56:13.802Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6381
File: src/lotus_json/actors/states/evm_state.rs:41-44
Timestamp: 2026-01-05T12:56:13.802Z
Learning: In Rust codebases (e.g., Forest), do not add #[cfg(test)] to functions already annotated with #[test]. The #[test] attribute ensures the function is compiled only for tests, so a separate #[cfg(test)] is redundant and can be removed if present. Apply this check to all Rust files that contain #[test] functions.

Applied to files:

  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/pubsub.rs
🧬 Code graph analysis (2)
src/rpc/methods/chain.rs (1)
src/rpc/methods/eth.rs (2)
  • from_filecoin_tipset (567-645)
  • e (1992-1992)
src/rpc/methods/eth/pubsub.rs (1)
src/rpc/methods/chain.rs (19)
  • handle (170-194)
  • handle (237-252)
  • handle (266-281)
  • handle (296-310)
  • handle (325-357)
  • handle (370-378)
  • handle (391-402)
  • handle (415-527)
  • handle (540-564)
  • handle (577-584)
  • handle (597-640)
  • handle (653-677)
  • handle (693-702)
  • handle (717-722)
  • handle (737-775)
  • handle (790-806)
  • chain (460-460)
  • chain (498-498)
  • new_heads (80-110)
⏰ 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). (9)
  • GitHub Check: Coverage
  • GitHub Check: Check
  • GitHub Check: Build Ubuntu
  • GitHub Check: Build MacOS
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: tests-release
  • GitHub Check: All lint checks
  • GitHub Check: rubocop
  • GitHub Check: Build forest binaries on Linux AMD64
🔇 Additional comments (2)
src/rpc/methods/eth.rs (2)

534-547: TxInfo + From<bool> is a nice API cleanup.

Clearer intent at call sites and avoids repeating boolean-to-meaning mapping logic.


1604-1666: Call sites updated cleanly to the new constructor and TxInfo mapping.

The switch to Block::from_filecoin_tipset(..., full_tx_info.into()).await is straightforward and keeps error mapping consistent.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@LesnyRumcajs LesnyRumcajs force-pushed the fix-eth-subscribe-heads branch from 8d05dcf to e58d247 Compare January 14, 2026 16:38
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 (1)
src/rpc/methods/chain.rs (1)

80-109: Change TxInfo::Full to TxInfo::Hash in the newHeads subscription and improve structured logging.

The newHeads subscription currently includes full transaction objects via TxInfo::Full, which increases payload size without functional benefit. In contrast, eth_getBlockByNumber methods expose a fullTxInfo parameter allowing callers to choose. The newHeads subscription should default to TxInfo::Hash, consistent with standard Ethereum RPC practice (geth, Infura, etc.) where subscriptions typically return block headers only, not transaction bodies. This reduces bandwidth and serialization overhead without sacrificing functionality.

Additionally, structured logging should include the tipset key for better debuggability, following the pattern already used in the logs function (line 139).

Proposed diff (Hash default + structured logging)
 pub(crate) fn new_heads<DB: Blockstore + Send + Sync + 'static>(
     data: Ctx<DB>,
 ) -> (Subscriber<ApiHeaders>, JoinHandle<()>) {
@@
         while let Ok(v) = subscriber.recv().await {
             let headers = match v {
                 HeadChange::Apply(ts) => {
+                    let ts_key = ts.key().clone();
                     // Convert the tipset to an Ethereum block with full transaction info
                     // Note: In Filecoin's Eth RPC, a tipset maps to a single Ethereum block
-                    match EthBlock::from_filecoin_tipset(data.clone(), ts, TxInfo::Full).await {
+                    match EthBlock::from_filecoin_tipset(data.clone(), ts, TxInfo::Hash).await {
                         Ok(block) => ApiHeaders(block),
                         Err(e) => {
-                            tracing::error!("Failed to convert tipset to eth block: {}", e);
+                            tracing::error!(error=?e, tipset_key=%ts_key, "Failed to convert tipset to eth block");
                             continue;
                         }
                     }
                 }
             };
♻️ Duplicate comments (1)
src/rpc/methods/eth/types.rs (1)

424-427: Add doc comment for public ApiHeaders (required by guidelines). As per coding guidelines, public structs should have doc comments.

Proposed diff
 #[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
+/// Newtype wrapper for `eth_subscribe` payloads.
+///
+/// Serialized via `crate::lotus_json` to emit an Ethereum-style `Block` object
+/// (instead of Filecoin tipset headers).
 pub struct ApiHeaders(#[serde(with = "crate::lotus_json")] pub Block);
🧹 Nitpick comments (1)
src/rpc/methods/eth.rs (1)

564-645: Avoid building full transactions when tx_info == TxInfo::Hash. Right now TxInfo::Hash still constructs full ApiEthTx objects, then converts to hashes; this is likely avoidable overhead. Also consider adding .context(...) around tipset execution/conversion steps for better error traces.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d05dcf and e58d247.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/pubsub.rs
  • src/rpc/methods/eth/types.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • src/rpc/methods/eth/pubsub.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Use anyhow::Result<T> for most operations and add context with .context() when operations fail
Use tokio::task::spawn_blocking for CPU-intensive work such as VM execution and cryptography operations
Use .get() instead of indexing with [index] for safe element access
Do not use unwrap() in production code; use ? or expect() with descriptive messages instead
Do not use dbg! macro in non-test Rust code
Do not use todo! macro in non-test Rust code
Use strum crate for enum string conversions
Use derive_more crate for common trait implementations
Document all public functions and structs with doc comments
Use #[cfg(test)] or #[cfg(feature = "doctest-private")] for test-only code
Use channel-based communication (flume, tokio channels) between async tasks
Use consistent formatting following cargo fmt standards
Ensure code passes cargo clippy --all-targets --no-deps -- --deny=warnings linter checks

Files:

  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: ChainSafe/forest PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-14T10:25:49.996Z
Learning: Publisher/Subscriber Pattern: Broadcast `HeadChange` events to multiple subscribers (RPC, message pool, chain indexer)
Learnt from: LesnyRumcajs
Repo: ChainSafe/forest PR: 5907
File: src/rpc/methods/state.rs:523-570
Timestamp: 2025-08-06T15:44:33.467Z
Learning: LesnyRumcajs prefers to rely on BufWriter's Drop implementation for automatic flushing rather than explicit flush() calls in Forest codebase.
📚 Learning: 2026-01-14T10:25:49.996Z
Learnt from: CR
Repo: ChainSafe/forest PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-14T10:25:49.996Z
Learning: Applies to **/*.rs : Document all public functions and structs with doc comments

Applied to files:

  • src/rpc/methods/eth/types.rs
📚 Learning: 2026-01-05T12:54:40.850Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6381
File: src/lotus_json/actors/states/cron_state.rs:8-8
Timestamp: 2026-01-05T12:54:40.850Z
Learning: In Rust code reviews, do not derive Eq for a struct if any field does not implement Eq (e.g., types from external dependencies). If a type like CronStateLotusJson includes fields wrapping external dependencies that lack Eq, derive PartialEq (or implement PartialEq manually) but avoid deriving Eq. This ensures comparisons compile and reflect actual equivalence semantics. When needed, consider implementing custom PartialEq (and possibly Eq) only after ensuring all fields (or wrappers) implement Eq, or keep PartialEq-only if full equality semantics cannot be expressed.

Applied to files:

  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
📚 Learning: 2026-01-05T12:56:13.802Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6381
File: src/lotus_json/actors/states/evm_state.rs:41-44
Timestamp: 2026-01-05T12:56:13.802Z
Learning: In Rust codebases (e.g., Forest), do not add #[cfg(test)] to functions already annotated with #[test]. The #[test] attribute ensures the function is compiled only for tests, so a separate #[cfg(test)] is redundant and can be removed if present. Apply this check to all Rust files that contain #[test] functions.

Applied to files:

  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
📚 Learning: 2026-01-14T10:25:49.996Z
Learnt from: CR
Repo: ChainSafe/forest PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-14T10:25:49.996Z
Learning: Publisher/Subscriber Pattern: Broadcast `HeadChange` events to multiple subscribers (RPC, message pool, chain indexer)

Applied to files:

  • src/rpc/methods/chain.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-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/rpc/methods/chain.rs
🧬 Code graph analysis (1)
src/rpc/methods/chain.rs (1)
src/rpc/methods/eth.rs (2)
  • from_filecoin_tipset (567-645)
  • e (1992-1992)
⏰ 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). (8)
  • GitHub Check: All lint checks
  • GitHub Check: Build Ubuntu
  • GitHub Check: Build MacOS
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: tests-release
  • GitHub Check: rubocop
  • GitHub Check: Coverage
  • GitHub Check: Build forest binaries on Linux AMD64
🔇 Additional comments (2)
src/rpc/methods/eth.rs (2)

534-547: TxInfo + From<bool> is a clean API improvement.


1604-1665: Updated handlers to use Block::from_filecoin_tipset(..., full_tx_info.into()) look consistent.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@LesnyRumcajs LesnyRumcajs added this pull request to the merge queue Jan 15, 2026
Merged via the queue into main with commit b7f3ec6 Jan 15, 2026
51 checks passed
@LesnyRumcajs LesnyRumcajs deleted the fix-eth-subscribe-heads branch January 15, 2026 07:44
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.

eth_subscribe returns wrong data structure for newHeads

3 participants