Skip to content

fix(RPC): backport F3 finality resolution to eth V1 methods#6644

Open
hanabi1224 wants to merge 17 commits intomainfrom
hm/f3-await-v1-rpc
Open

fix(RPC): backport F3 finality resolution to eth V1 methods#6644
hanabi1224 wants to merge 17 commits intomainfrom
hm/f3-await-v1-rpc

Conversation

@hanabi1224
Copy link
Contributor

@hanabi1224 hanabi1224 commented Feb 24, 2026

Summary of changes

Changes introduced in this pull request:

Reference issue to close (if applicable)

Closes #6631

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.

Outside contributions

  • I have read and agree to the CONTRIBUTING document.
  • I have read and agree to the AI Policy document. I understand that failure to comply with the guidelines will lead to rejection of the pull request.

Summary by CodeRabbit

  • New Features

    • Backported F3 finality resolution to ETH RPCs; unified block-parameter handling and a new ChainGetTipSet RPC for more consistent responses.
    • API-version-aware tipset resolution with safe/finalized/latest/pending handling and an env var toggle for v1 behavior.
  • Documentation

    • Added FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION env var docs.
    • Added three new Tipset tokens to the dictionary.
    • Added changelog entry for Forest v0.32.2.
  • Tests

    • Updated RPC snapshots and test flows to align with the new resolution behavior.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Backports F3 finality resolution into ETH v1 RPCs, adds a TipsetResolver and BlockNumberOrHash flow, converts many ETH RPC handlers to resolver-based async tipset lookup, changes per-request API path to a single ApiPaths value, updates tests/snapshots, and documents an env var to toggle V1 F3 behavior.

Changes

Cohort / File(s) Summary
Changelog & Docs
CHANGELOG.md, docs/dictionary.txt, docs/docs/users/reference/env_variables.md
Add changelog entry, add tipset tokens (v0/v1/v2) and document FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION.
ETH tipset resolver & types
src/rpc/methods/eth/tipset_resolver.rs, src/rpc/methods/eth.rs, src/rpc/methods/eth/types.rs
Introduce TipsetResolver (API-version-aware), add/expand BlockNumberOrHash/Predefined with async resolution helpers, re-export resolver, and convert many ETH RPCs to use resolver-based tipset resolution (v1/v2-aware).
Chain tipset helpers
src/rpc/methods/chain.rs, src/eth/mod.rs
Move/make SAFE_HEIGHT_DISTANCE public in chain module and update ChainGetTipSet flows to return concrete Tipset; remove SAFE_EPOCH_DELAY constant from src/eth/mod.rs.
Request / API path model
src/rpc/request.rs, src/rpc/reflect/mod.rs, src/rpc/mod.rs, src/rpc/client.rs
Switch per-request api_paths: BitFlags<ApiPaths> → single api_path: ApiPaths; add Request::set_api_path/with_api_path; update RpcMethodExt request signatures and call sites to resolve ApiPaths at runtime; remove some V2 registrations.
ETH tests & snapshots
src/tool/subcommands/api_cmd/api_compare_tests.rs, src/tool/subcommands/api_cmd/test_snapshot.rs, src/tool/subcommands/api_cmd/test_snapshots.txt
Update tests to use BlockNumberOrHash/Predefined, add SAFE_EPOCH_DELAY_FOR_TESTING, propagate errors (Result) in builders, adjust assertions, and add/remove snapshots covering F1/F2/F3 and finality tags.
CLI / tooling matching
src/tool/subcommands/api_cmd.rs
DumpTests filtering updated to use exact api_path equality (pattern binding renamed from api_paths to api_path).
Misc small edits
src/rpc/client.rs, other small files
Remove runtime max_api_path mapping in one client site, adjust reflect/request conventions and signature changes, minor visibility/exports and mapping updates.

Sequence Diagram

sequenceDiagram
    autonumber
    participant Client as rgba(52,152,219,0.5)
    participant Handler as rgba(46,204,113,0.5)
    participant Resolver as rgba(155,89,182,0.5)
    participant Chain as rgba(241,196,15,0.5)

    Client->>Handler: send eth_* request (params, block_param, api_path)
    Handler->>Resolver: construct BlockNumberOrHash from block_param
    Resolver->>Resolver: choose resolution path (Predefined / Number / Hash) based on api_path and env toggle
    alt Predefined -> needs finality
        Resolver->>Chain: request predefined tipset (safe/finalized via ChainGetTipSet)
        Chain-->>Resolver: Tipset
    else Number or Hash
        Resolver->>Chain: lookup tipset by number or hash
        Chain-->>Resolver: Tipset
    end
    Resolver-->>Handler: resolved Tipset
    Handler->>Handler: produce response using Tipset
    Handler-->>Client: return RPC response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • sudo-shashank
  • akaladarshi
  • LesnyRumcajs
🚥 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 PR title 'fix(RPC): backport F3 finality resolution to eth V1 methods' clearly summarizes the main objective of adding F3 finality resolution support to Ethereum V1 RPC methods as required by issue #6631.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from issue #6631: adds 'safe' and 'finalized' support to V1 APIs via new TipsetResolver with environment-driven F3 toggle, ensures V1 and V2 alignment, and includes changelog/documentation updates for RPC parity with Lotus.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing F3 finality resolution in V1 APIs per issue #6631. Changes include TipsetResolver implementation, BlockNumberOrHash refactoring, test snapshots, documentation, and ChainGetTipSet support—all within scope of the stated objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hm/f3-await-v1-rpc

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

@hanabi1224 hanabi1224 added the RPC requires calibnet RPC checks to run on CI label Feb 24, 2026
@hanabi1224 hanabi1224 changed the title fix(RPC): backport F3 fanality resolution to eth V1 methods fix(RPC): backport F3 finality resolution to eth V1 methods Feb 24, 2026
@hanabi1224 hanabi1224 marked this pull request as ready for review February 24, 2026 16:01
@hanabi1224 hanabi1224 requested a review from a team as a code owner February 24, 2026 16:01
@hanabi1224 hanabi1224 requested review from akaladarshi and sudo-shashank and removed request for a team February 24, 2026 16:01
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.

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/eth.rs (1)

2712-2718: ⚠️ Potential issue | 🟠 Major

Bug: V1 handler EthGetTransactionByBlockNumberAndIndex uses tipset_by_block_number_or_hash_v2 instead of _v1.

This method has API_PATHS: BitFlags<ApiPaths> = ApiPaths::all() (non-V2), yet it calls tipset_by_block_number_or_hash_v2. All other V1 handlers in this PR consistently use _v1. The V2 counterpart at line 2740 also correctly uses _v2. This means V1 callers of this endpoint won't respect the FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION env flag.

Proposed fix
         let ts = BlockNumberOrHash::from(block_param)
-            .tipset_by_block_number_or_hash_v2(&ctx, ResolveNullTipset::TakeOlder)
+            .tipset_by_block_number_or_hash_v1(&ctx, ResolveNullTipset::TakeOlder)
             .await?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth.rs` around lines 2712 - 2718, The V1 handler
EthGetTransactionByBlockNumberAndIndex currently calls
tipset_by_block_number_or_hash_v2 which bypasses V1 feature gating; change the
call in the async fn handle (the BlockNumberOrHash::from(block_param) chain) to
use tipset_by_block_number_or_hash_v1 with the same ResolveNullTipset::TakeOlder
and ctx so V1 callers respect FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION and
match other V1 handlers.
🧹 Nitpick comments (10)
docs/docs/users/reference/env_variables.md (1)

61-61: Tighten phrasing in the description.

Consider shortening to “Whether to disable …” for concision.

✍️ Suggested tweak
-| `FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION`            | 1 or true                        | empty                                          | 1                                                             | Whether or not to disable F3 finality resolution in Eth v1 RPC methods                                                |
+| `FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION`            | 1 or true                        | empty                                          | 1                                                             | Whether to disable F3 finality resolution in Eth v1 RPC methods                                                       |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/docs/users/reference/env_variables.md` at line 61, Update the
description for the environment variable
FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION to a more concise phrasing: replace
"Whether or not to disable F3 finality resolution in Eth v1 RPC methods" with
"Whether to disable F3 finality resolution in Eth v1 RPC methods" in the docs
table row to tighten the wording.
src/rpc/methods/eth/types.rs (1)

270-275: Add a doc comment for the public enum.

BlockNumberOrPredefined is public; please add a short doc comment describing its purpose.

📝 Suggested doc comment
+/// Block selector used by eth RPCs; accepts either a predefined tag or a block number.
 #[derive(PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema, derive_more::From)]
 #[serde(untagged)]
 pub enum BlockNumberOrPredefined {

As per coding guidelines “Document all public functions and structs with doc comments”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth/types.rs` around lines 270 - 275, The public enum
BlockNumberOrPredefined lacks a doc comment; add a concise Rust doc comment
(///) above the enum BlockNumberOrPredefined explaining that it represents
either an explicit block number (BlockNumber variant holding EthInt64) or a
predefined block specifier (PredefinedBlock variant holding Predefined), and
mention its serialization behavior if relevant (serde untagged) so callers
understand its usage in RPC types.
src/tool/subcommands/api_cmd/test_snapshots.txt (1)

143-148: Verify new snapshot artifacts are available remotely.

Please confirm these new snapshot files have been uploaded to the snapshot bucket; otherwise the fetch step will fail for these test cases.

Based on learnings “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.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tool/subcommands/api_cmd/test_snapshots.txt` around lines 143 - 148, The
test snapshot manifest src/tool/subcommands/api_cmd/test_snapshots.txt lists
several .rpcsnap.json.zst artifacts (e.g.,
filecoin_filecoinaddresstoethaddress_1771943760273649.rpcsnap.json.zst,
_1771943760275947..., _1771943760315677..., _1771946498262342...,
_1771946498262483..., _1771946498262885...) but these are not stored in-repo and
must exist in the remote snapshot bucket; verify each of these exact files has
been uploaded to the DigitalOcean snapshot bucket, ensure the bucket
manifest/catalog used by the fetch step includes these entries, confirm object
ACLs/public access or credentials permit the fetch, and if any are missing
upload them (or update the manifest file referenced by the fetch logic) so the
test fetch step succeeds.
src/tool/subcommands/api_cmd/api_compare_tests.rs (1)

1475-1483: Minor inconsistency: use from_predefined instead of the variant directly.

Every other call site in this file uses BlockNumberOrHash::from_predefined(tag). The direct variant form works, but it exposes the internal enum structure unnecessarily.

♻️ Suggested fix
-                        (msg.clone(), BlockNumberOrHash::PredefinedBlock(tag)),
+                        (msg.clone(), BlockNumberOrHash::from_predefined(tag)),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs` around lines 1475 - 1483,
Replace the direct enum construction BlockNumberOrHash::PredefinedBlock(tag)
with the helper constructor BlockNumberOrHash::from_predefined(tag) in the loop
that builds tests (inside the for tag in [Predefined::Latest, Predefined::Safe,
Predefined::Finalized] block). Update the EthCallV2::request_with_alias call
used when creating RpcTest::identity so it passes
BlockNumberOrHash::from_predefined(tag) instead of the PredefinedBlock variant
to match other call sites and avoid exposing the enum internals.
src/rpc/methods/chain.rs (2)

1049-1049: Consider simplifying with .map(Some) instead of Some(…).transpose().

Both are semantically identical — Result<Tipset>Result<Option<Tipset>> — but .map(Some) is more idiomatic and easier to read at a glance:

-            TipsetTag::Finalized => Some(Self::get_latest_finalized_tipset(ctx).await).transpose(),
+            TipsetTag::Finalized => Self::get_latest_finalized_tipset(ctx).await.map(Some),

(The Safe arm on line 1050 uses the same existing pattern; both could be changed for consistency, or left as-is — it's a pure readability preference.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/chain.rs` at line 1049, The match arm using
TipsetTag::Finalized currently wraps an awaited Result in Some(...).transpose();
replace that pattern by mapping the Result to an Option via .map(Some) on the
async call (i.e., change the expression involving
get_latest_finalized_tipset(ctx).await to
get_latest_finalized_tipset(ctx).await.map(Some)); do the same for the Safe arm
that uses get_latest_safe_tipset(ctx).await so both arms use the more idiomatic
.map(Some) conversion from Result<Tipset> to Result<Option<Tipset>>.

1071-1077: New public function is missing a doc comment (required by coding guidelines).

A brief description clarifying this is the EC-only (non-F3) safe-tipset path would also aid consumers in eth.rs.

📝 Suggested doc comment
+    /// Returns the EC-based safe tipset, i.e. the tipset at [`SAFE_HEIGHT_DISTANCE`] epochs
+    /// behind the current head, without consulting F3. Use [`get_latest_safe_tipset`] when
+    /// F3 finality should be preferred.
     pub fn get_ec_safe_tipset(ctx: &Ctx<impl Blockstore>) -> anyhow::Result<Tipset> {

As per coding guidelines: "Document all public functions and structs with doc comments".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/chain.rs` around lines 1071 - 1077, Add a Rust doc comment
(///) to the public function get_ec_safe_tipset describing its purpose and
behaviour: state that it returns the EC-only (non-F3) safe tipset by computing
safe_height = head.epoch() - SAFE_HEIGHT_DISTANCE and resolving the tipset at
that height with ResolveNullTipset::TakeOlder; note that this is the EC-only
safe-tipset path (not F3) and mention that consumers like eth.rs rely on this
contract. Ensure the comment sits immediately above the get_ec_safe_tipset
declaration and follows crate doc style and tone.
src/rpc/methods/eth.rs (4)

362-377: resolve_common_predefined_tipset returns Ok(None) for Safe and Finalized, delegating to callers.

The wildcard arm _ => Ok(None) matches Safe and Finalized, which is correct since these are handled by the caller (resolve_predefined_tipset_v1 / _v2). However, if new variants are ever added to Predefined, they'd silently fall through here too.

Consider using an explicit match instead of the wildcard to get a compile-time reminder if new variants are added:

Suggested change
-            _ => Ok(None),
+            Predefined::Safe | Predefined::Finalized => Ok(None),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth.rs` around lines 362 - 377, The match in
resolve_common_predefined_tipset currently uses a wildcard arm that returns
Ok(None), which will silently accept any future Predefined variants; change the
match to explicitly list all known variants (Earliest, Pending, Latest, Safe,
Finalized) and return Ok(None) for Safe and Finalized so the compiler will error
if a new variant is added, updating the match in the
resolve_common_predefined_tipset function to remove the `_ => Ok(None)` arm and
replace it with explicit arms for Safe and Finalized (and keep the existing
behavior for Earliest, Pending, Latest).

439-452: BlockNumberOrHash::from_str shadows FromStr::from_str.

The method from_str is defined as an inherent method (not implementing the FromStr trait), which silently shadows any potential FromStr trait implementation. This is fine functionally, but if FromStr is ever derived or implemented, the inherent method will always take precedence, which could cause confusion.

Also, the addition of "safe" and "finalized" parsing at lines 444-445 is correct and aligns with the PR objective.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth.rs` around lines 439 - 452, The current inherent method
BlockNumberOrHash::from_str shadows the standard FromStr trait; replace it by
implementing std::str::FromStr for BlockNumberOrHash (impl FromStr for
BlockNumberOrHash { type Err = Error; fn from_str(&str) -> Result<Self,
Self::Err> { ... } }) reusing the existing logic (keep the "earliest",
"pending", "latest"/"", "safe", "finalized" branches and the hex branch that
calls hex_str_to_epoch and then from_block_number/from_predefined); remove or
rename the original inherent from_str to avoid shadowing (e.g., rename to
parse_str) so there is no ambiguity between the trait impl and an inherent
method.

3857-3865: EthTraceCall serves V1 and V2 but always uses _v2 resolution.

This single handler serves both V1 | V2 paths. Using tipset_by_block_number_or_hash_v2 means V1 callers passing "safe" or "finalized" as block_param will get F3-based resolution regardless of FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION. The default case ("latest") is unaffected since both paths resolve it identically.

If the intent is to respect the env flag for V1, consider either splitting into V1/V2 handlers (like most other methods in this PR) or passing the API version dynamically.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth.rs` around lines 3857 - 3865, The handler for
EthTraceCall always uses tipset_by_block_number_or_hash_v2, causing V1 callers
using BlockNumberOrHash values like "safe" or "finalized" to get F3 resolution
regardless of FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION; update the handler
to respect API version by branching on the request version (or split into
separate V1/V2 handlers): if the caller is V1 use tipset_by_block_number_or_hash
(or the legacy resolver) when block_param is "safe"|"finalized", otherwise use
tipset_by_block_number_or_hash_v2 for V2; adjust the code around
EthTraceCall::handle, the BlockNumberOrHash parsing, and the call to
tipset_by_block_number_or_hash_v2 (and ResolveNullTipset::TakeOlder) so V1 flows
consult the env flag and legacy resolver while V2 continues to use the _v2
resolver.

3186-3191: FilecoinAddressToEthAddress defaults to Predefined::Finalized and always uses _v2.

This endpoint is served on all_with_v2() but always resolves via tipset_by_block_number_or_hash_v2. V1 callers (who omit block_param) will always get F3-finalized resolution, bypassing the FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION opt-out. Same concern as EthTraceCall – this may be intentional since there's no separate V2 handler, but it's inconsistent with the V1 backport pattern used elsewhere in this PR.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth.rs` around lines 3186 - 3191, The
FilecoinAddressToEthAddress handler currently defaults block_param to
Predefined::Finalized and always calls tipset_by_block_number_or_hash_v2,
causing V1 callers (served via all_with_v2()) to be forced to F3-finalized
resolution and bypass the FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION opt-out;
update the logic in the FilecoinAddressToEthAddress handler to detect V1
requests (as other V1 backports do) and: if V1 and the
FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION flag is set, avoid defaulting to
Predefined::Finalized and call the non-_v2 resolver
(tipset_by_block_number_or_hash or the v1 variant) instead; otherwise keep the
existing v2 path (leave the block_param default and call
tipset_by_block_number_or_hash_v2) so v2 behavior remains unchanged. Reference
symbols: FilecoinAddressToEthAddress, block_param, Predefined::Finalized,
tipset_by_block_number_or_hash_v2, all_with_v2(), and
FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/rpc/methods/eth.rs`:
- Around line 2712-2718: The V1 handler EthGetTransactionByBlockNumberAndIndex
currently calls tipset_by_block_number_or_hash_v2 which bypasses V1 feature
gating; change the call in the async fn handle (the
BlockNumberOrHash::from(block_param) chain) to use
tipset_by_block_number_or_hash_v1 with the same ResolveNullTipset::TakeOlder and
ctx so V1 callers respect FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION and match
other V1 handlers.

---

Nitpick comments:
In `@docs/docs/users/reference/env_variables.md`:
- Line 61: Update the description for the environment variable
FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION to a more concise phrasing: replace
"Whether or not to disable F3 finality resolution in Eth v1 RPC methods" with
"Whether to disable F3 finality resolution in Eth v1 RPC methods" in the docs
table row to tighten the wording.

In `@src/rpc/methods/chain.rs`:
- Line 1049: The match arm using TipsetTag::Finalized currently wraps an awaited
Result in Some(...).transpose(); replace that pattern by mapping the Result to
an Option via .map(Some) on the async call (i.e., change the expression
involving get_latest_finalized_tipset(ctx).await to
get_latest_finalized_tipset(ctx).await.map(Some)); do the same for the Safe arm
that uses get_latest_safe_tipset(ctx).await so both arms use the more idiomatic
.map(Some) conversion from Result<Tipset> to Result<Option<Tipset>>.
- Around line 1071-1077: Add a Rust doc comment (///) to the public function
get_ec_safe_tipset describing its purpose and behaviour: state that it returns
the EC-only (non-F3) safe tipset by computing safe_height = head.epoch() -
SAFE_HEIGHT_DISTANCE and resolving the tipset at that height with
ResolveNullTipset::TakeOlder; note that this is the EC-only safe-tipset path
(not F3) and mention that consumers like eth.rs rely on this contract. Ensure
the comment sits immediately above the get_ec_safe_tipset declaration and
follows crate doc style and tone.

In `@src/rpc/methods/eth.rs`:
- Around line 362-377: The match in resolve_common_predefined_tipset currently
uses a wildcard arm that returns Ok(None), which will silently accept any future
Predefined variants; change the match to explicitly list all known variants
(Earliest, Pending, Latest, Safe, Finalized) and return Ok(None) for Safe and
Finalized so the compiler will error if a new variant is added, updating the
match in the resolve_common_predefined_tipset function to remove the `_ =>
Ok(None)` arm and replace it with explicit arms for Safe and Finalized (and keep
the existing behavior for Earliest, Pending, Latest).
- Around line 439-452: The current inherent method BlockNumberOrHash::from_str
shadows the standard FromStr trait; replace it by implementing std::str::FromStr
for BlockNumberOrHash (impl FromStr for BlockNumberOrHash { type Err = Error; fn
from_str(&str) -> Result<Self, Self::Err> { ... } }) reusing the existing logic
(keep the "earliest", "pending", "latest"/"", "safe", "finalized" branches and
the hex branch that calls hex_str_to_epoch and then
from_block_number/from_predefined); remove or rename the original inherent
from_str to avoid shadowing (e.g., rename to parse_str) so there is no ambiguity
between the trait impl and an inherent method.
- Around line 3857-3865: The handler for EthTraceCall always uses
tipset_by_block_number_or_hash_v2, causing V1 callers using BlockNumberOrHash
values like "safe" or "finalized" to get F3 resolution regardless of
FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION; update the handler to respect API
version by branching on the request version (or split into separate V1/V2
handlers): if the caller is V1 use tipset_by_block_number_or_hash (or the legacy
resolver) when block_param is "safe"|"finalized", otherwise use
tipset_by_block_number_or_hash_v2 for V2; adjust the code around
EthTraceCall::handle, the BlockNumberOrHash parsing, and the call to
tipset_by_block_number_or_hash_v2 (and ResolveNullTipset::TakeOlder) so V1 flows
consult the env flag and legacy resolver while V2 continues to use the _v2
resolver.
- Around line 3186-3191: The FilecoinAddressToEthAddress handler currently
defaults block_param to Predefined::Finalized and always calls
tipset_by_block_number_or_hash_v2, causing V1 callers (served via all_with_v2())
to be forced to F3-finalized resolution and bypass the
FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION opt-out; update the logic in the
FilecoinAddressToEthAddress handler to detect V1 requests (as other V1 backports
do) and: if V1 and the FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION flag is set,
avoid defaulting to Predefined::Finalized and call the non-_v2 resolver
(tipset_by_block_number_or_hash or the v1 variant) instead; otherwise keep the
existing v2 path (leave the block_param default and call
tipset_by_block_number_or_hash_v2) so v2 behavior remains unchanged. Reference
symbols: FilecoinAddressToEthAddress, block_param, Predefined::Finalized,
tipset_by_block_number_or_hash_v2, all_with_v2(), and
FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION.

In `@src/rpc/methods/eth/types.rs`:
- Around line 270-275: The public enum BlockNumberOrPredefined lacks a doc
comment; add a concise Rust doc comment (///) above the enum
BlockNumberOrPredefined explaining that it represents either an explicit block
number (BlockNumber variant holding EthInt64) or a predefined block specifier
(PredefinedBlock variant holding Predefined), and mention its serialization
behavior if relevant (serde untagged) so callers understand its usage in RPC
types.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs`:
- Around line 1475-1483: Replace the direct enum construction
BlockNumberOrHash::PredefinedBlock(tag) with the helper constructor
BlockNumberOrHash::from_predefined(tag) in the loop that builds tests (inside
the for tag in [Predefined::Latest, Predefined::Safe, Predefined::Finalized]
block). Update the EthCallV2::request_with_alias call used when creating
RpcTest::identity so it passes BlockNumberOrHash::from_predefined(tag) instead
of the PredefinedBlock variant to match other call sites and avoid exposing the
enum internals.

In `@src/tool/subcommands/api_cmd/test_snapshots.txt`:
- Around line 143-148: The test snapshot manifest
src/tool/subcommands/api_cmd/test_snapshots.txt lists several .rpcsnap.json.zst
artifacts (e.g.,
filecoin_filecoinaddresstoethaddress_1771943760273649.rpcsnap.json.zst,
_1771943760275947..., _1771943760315677..., _1771946498262342...,
_1771946498262483..., _1771946498262885...) but these are not stored in-repo and
must exist in the remote snapshot bucket; verify each of these exact files has
been uploaded to the DigitalOcean snapshot bucket, ensure the bucket
manifest/catalog used by the fetch step includes these entries, confirm object
ACLs/public access or credentials permit the fetch, and if any are missing
upload them (or update the manifest file referenced by the fetch logic) so the
test fetch step succeeds.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 442e716 and c3496cd.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • docs/dictionary.txt
  • docs/docs/users/reference/env_variables.md
  • src/eth/mod.rs
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/types.rs
  • src/tool/subcommands/api_cmd/api_compare_tests.rs
  • src/tool/subcommands/api_cmd/test_snapshot.rs
  • src/tool/subcommands/api_cmd/test_snapshots.txt
💤 Files with no reviewable changes (1)
  • src/eth/mod.rs

@codecov
Copy link

codecov bot commented Feb 24, 2026

Codecov Report

❌ Patch coverage is 59.47137% with 92 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.38%. Comparing base (4f8f41a) to head (913f7ad).

Files with missing lines Patch % Lines
src/rpc/methods/eth.rs 59.48% 15 Missing and 32 partials ⚠️
src/rpc/methods/eth/tipset_resolver.rs 60.52% 28 Missing and 2 partials ⚠️
src/rpc/request.rs 12.50% 7 Missing ⚠️
src/rpc/methods/chain.rs 72.72% 2 Missing and 1 partial ⚠️
src/rpc/reflect/mod.rs 80.00% 1 Missing and 1 partial ⚠️
src/tool/subcommands/api_cmd.rs 0.00% 2 Missing ⚠️
src/rpc/client.rs 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
Files with missing lines Coverage Δ
src/rpc/methods/eth/types.rs 75.56% <100.00%> (ø)
src/rpc/mod.rs 87.64% <ø> (-0.78%) ⬇️
src/tool/subcommands/api_cmd/test_snapshot.rs 85.31% <ø> (ø)
src/rpc/client.rs 46.37% <50.00%> (-0.04%) ⬇️
src/rpc/reflect/mod.rs 69.45% <80.00%> (+0.80%) ⬆️
src/tool/subcommands/api_cmd.rs 0.00% <0.00%> (ø)
src/rpc/methods/chain.rs 53.09% <72.72%> (-0.10%) ⬇️
src/rpc/request.rs 41.37% <12.50%> (-18.63%) ⬇️
src/rpc/methods/eth/tipset_resolver.rs 60.52% <60.52%> (ø)
src/rpc/methods/eth.rs 68.19% <59.48%> (-1.24%) ⬇️

... and 6 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 4f8f41a...913f7ad. Read the comment docs.

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

Copy link
Collaborator

@akaladarshi akaladarshi left a comment

Choose a reason for hiding this comment

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

@hanabi1224 Couple of things here, I think there’s an opportunity to simplify the design:

Right now it feels a bit complex, with a lot of functions around enum variants. I wonder if we can centralise some of this logic.

  1. Consider introducing a tipsetResolve here which can help us resolve all the enum variants based on the env and params.

  2. We could move the F3/EC related tag resolution to the tipsetResolver, keeping ChainGetTipSetV2 focused purely on API responsibilities.

  3. It would be good to structure this in a way that avoids unnecessary refactors once ETH_V1_DISABLE_F3_FINALITY_RESOLUTION_ENV_KEY is removed: filecoin-project/lotus#13315

  4. EthGetTransactionByBlockNumberAndIndex still uses the tipset_by_block_number_or_hash_v2.

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: 2

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)

1129-1139: ⚠️ Potential issue | 🟠 Major

Fallback to EC finality is missing on F3 tipset load errors.

At Line 1129, failure to load the F3 finalized tipset becomes a hard error. That diverges from ChainGetFinalizedTipset, which falls back to EC finality when F3 resolution fails.

Suggested fix
-        let ts = ctx
-            .chain_index()
-            .load_required_tipset(&f3_finalized_head.key)
-            .map_err(|e| {
-                anyhow::anyhow!(
-                    "Failed to load F3 finalized tipset at epoch {} with key {}: {e}",
-                    f3_finalized_head.epoch,
-                    f3_finalized_head.key,
-                )
-            })?;
-        Ok(ts)
+        match ctx.chain_index().load_required_tipset(&f3_finalized_head.key) {
+            Ok(ts) => Ok(ts),
+            Err(e) => {
+                tracing::warn!(
+                    "Failed to load F3 finalized tipset at epoch {} with key {}: {e}; falling back to EC finality",
+                    f3_finalized_head.epoch,
+                    f3_finalized_head.key
+                );
+                Self::get_ec_finalized_tipset(ctx)
+            }
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/chain.rs` around lines 1129 - 1139, The code currently treats
failures to load the F3 finalized tipset (the call to
ctx.chain_index().load_required_tipset(&f3_finalized_head.key)) as a hard error;
change this to fall back to EC finality like ChainGetFinalizedTipset does by
catching the error from loading the F3 tipset, logging the failure (including
the original error and f3_finalized_head info), and then attempting to load the
EC tipset via ctx.chain_index().load_required_tipset(&ec_finalized_head.key); if
the EC load succeeds return that tipset, otherwise return the combined/last
error. Ensure you reference f3_finalized_head, ec_finalized_head, and
ctx.chain_index().load_required_tipset in the fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/rpc/methods/eth.rs`:
- Around line 3233-3238: The shared V1/V2 handler is forcing V2 tipset
resolution by calling
BlockNumberOrHash::from(...).tipset_by_block_number_or_hash_v2(...); replace
that call with the non-_v2 resolver (e.g., tipset_by_block_number_or_hash) so
resolution follows the request API path and respects V1 finality toggles; update
both occurrences (the call at
BlockNumberOrHash::from(...).tipset_by_block_number_or_hash_v2(&ctx,
ResolveNullTipset::TakeOlder).await? and the similar call in the other block) to
use the shared resolver and keep the same ResolveNullTipset argument unless a
different policy is required by the request path.
- Around line 2748-2750: The V1 handler EthGetTransactionByBlockNumberAndIndex
is incorrectly calling tipset_by_block_number_or_hash_v2 (which uses V2
semantics); change the call on the BlockNumberOrHash conversion path so it
invokes the V1 resolver (tipset_by_block_number_or_hash) instead and preserve
the original ResolveNullTipset behavior for the V1 flow (ensure the same
ResolveNullTipset::TakeOlder argument is passed and error handling remains
unchanged for the ts variable resolution).

---

Outside diff comments:
In `@src/rpc/methods/chain.rs`:
- Around line 1129-1139: The code currently treats failures to load the F3
finalized tipset (the call to
ctx.chain_index().load_required_tipset(&f3_finalized_head.key)) as a hard error;
change this to fall back to EC finality like ChainGetFinalizedTipset does by
catching the error from loading the F3 tipset, logging the failure (including
the original error and f3_finalized_head info), and then attempting to load the
EC tipset via ctx.chain_index().load_required_tipset(&ec_finalized_head.key); if
the EC load succeeds return that tipset, otherwise return the combined/last
error. Ensure you reference f3_finalized_head, ec_finalized_head, and
ctx.chain_index().load_required_tipset in the fix.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3496cd and 8e9eb08.

⛔ Files ignored due to path filters (3)
  • src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap is excluded by !**/*.snap
  • src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap is excluded by !**/*.snap
  • src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
  • src/tool/subcommands/api_cmd/test_snapshot.rs

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.

♻️ Duplicate comments (2)
src/rpc/methods/eth.rs (2)

2722-2724: ⚠️ Potential issue | 🟠 Major

Use V1 tipset resolution in the V1 transaction-by-block handler.

Line 2723 still calls tipset_by_block_number_or_hash_v2 inside EthGetTransactionByBlockNumberAndIndex (V1 API path set), which bypasses V1-specific finality behavior.

Suggested fix
-        let ts = BlockNumberOrHash::from(block_param)
-            .tipset_by_block_number_or_hash_v2(&ctx, ResolveNullTipset::TakeOlder)
+        let ts = BlockNumberOrHash::from(block_param)
+            .tipset_by_block_number_or_hash_v1(&ctx, ResolveNullTipset::TakeOlder)
             .await?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth.rs` around lines 2722 - 2724, In
EthGetTransactionByBlockNumberAndIndex, the V1 handler currently calls
BlockNumberOrHash::from(...).tipset_by_block_number_or_hash_v2(...), which uses
V2 finality rules; replace the call to use the V1 resolver by invoking
tipset_by_block_number_or_hash(...) instead (keep the same
ResolveNullTipset::TakeOlder and ctx/await usage) so the V1 path uses the V1
tipset resolution semantics.

3896-3904: ⚠️ Potential issue | 🟠 Major

Shared EthTraceCall still hardcodes V2 resolution.

Line 3903 always uses V2 resolution even though this method serves both V1 and V2 paths, so V1 calls do not honor V1 semantics.

Suggested fix
-    async fn handle(
-        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
-        (tx, trace_types, block_param): Self::Params,
-        _: &http::Extensions,
-    ) -> Result<Self::Ok, ServerError> {
+    async fn handle(
+        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
+        (tx, trace_types, block_param): Self::Params,
+        ext: &http::Extensions,
+    ) -> Result<Self::Ok, ServerError> {
@@
-        let ts = block_param
-            .tipset_by_block_number_or_hash_v2(&ctx, ResolveNullTipset::TakeOlder)
-            .await?;
+        let ts = block_param
+            .tipset_by_block_number_or_hash(
+                &ctx,
+                ResolveNullTipset::TakeOlder,
+                Self::api_path(ext)?,
+            )
+            .await?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth.rs` around lines 3896 - 3904, The handler always calls
tipset_by_block_number_or_hash_v2 which forces V2 semantics for all requests;
change it to pick the resolution function based on the incoming trace version by
checking the trace_types parameter (e.g., inspect trace_types or its
discriminator) and call tipset_by_block_number_or_hash (V1) for V1 requests and
tipset_by_block_number_or_hash_v2 (V2) for V2 requests, preserving the
appropriate ResolveNullTipset behavior for each path (e.g., keep
ResolveNullTipset::TakeOlder for V2 and the V1 semantics for V1).
🧹 Nitpick comments (5)
src/rpc/request.rs (1)

35-42: Document new public API-path mutators.

set_api_path and with_api_path are public and should have doc comments.

As per coding guidelines, "Document all public functions and structs with doc comments."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/request.rs` around lines 35 - 42, Add doc comments for the public
methods set_api_path and with_api_path: document that set_api_path(&mut self,
api_path: ApiPaths) sets the request's API path to the provided ApiPaths value
and mention mutates self, and document that with_api_path(self, api_path:
ApiPaths) -> Self is a builder-style convenience that sets the API path and
returns the modified Request for chaining; include parameter descriptions for
api_path and a brief example or usage note if appropriate. Ensure the comments
are placed immediately above the function signatures for set_api_path and
with_api_path and follow the crate's doc-comment style (///).
src/rpc/reflect/mod.rs (1)

367-371: Document the new api_path helper in RpcMethodExt.

This helper is part of the public trait surface and should include rustdoc.

As per coding guidelines, "Document all public functions and structs with doc comments."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/reflect/mod.rs` around lines 367 - 371, Add a rustdoc comment for the
new api_path helper in the RpcMethodExt trait: above fn api_path(ext:
&http::Extensions) -> anyhow::Result<ApiPaths> add a short summary of what the
helper does, document the `ext` parameter (that it reads ApiPaths from
http::Extensions), state the return value (Ok(ApiPaths) or Err on missing/copy
failure) and mention the error context ("failed to resolve api path") so callers
understand failure modes; ensure the doc follows crate guidelines for public
trait items and includes examples or usage notes if helpful.
src/rpc/methods/eth.rs (1)

307-373: Add rustdoc for newly public resolver APIs.

Lines 308, 319, 343, 358, 404, 450, 459, and 468 introduce public methods without doc comments.

As per coding guidelines, "Document all public functions and structs with doc comments."

Also applies to: 402-473

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth.rs` around lines 307 - 373, Add doc comments for the
newly public resolver APIs: impl Predefined methods resolve_predefined_tipset,
resolve_predefined_tipset_v1, resolve_predefined_tipset_v2, and
resolve_common_predefined_tipset (and any other public functions in this impl)
describing their purpose, parameters (ctx, api_version or chain_store), behavior
(how V1 vs V2 resolution works, env toggle for F3 finality, and what each
Predefined variant returns or errors), and the returned Tipset/result. Keep
comments brief, follow existing crate doc style (///), include examples or
panics/errors only if helpful, and ensure every public function mentioned in
this diff has a top-level rustdoc line.
src/tool/subcommands/api_cmd/api_compare_tests.rs (2)

1475-1483: Use BlockNumberOrHash::from_predefined(tag) instead of the direct variant constructor.

Line 1478 constructs BlockNumberOrHash::PredefinedBlock(tag) directly, while the rest of the file consistently uses the from_predefined constructor (see lines 1408, 1468, 1640, etc.). The from_predefined factory method is the public API per src/rpc/methods/eth/types.rs:403-405.

♻️ Proposed fix
-                        (msg.clone(), BlockNumberOrHash::PredefinedBlock(tag)),
+                        (msg.clone(), BlockNumberOrHash::from_predefined(tag)),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs` around lines 1475 - 1483,
Replace the direct construction BlockNumberOrHash::PredefinedBlock(tag) with the
public factory BlockNumberOrHash::from_predefined(tag) inside the loop that
builds RpcTest::identity entries; update the EthCallV2::request_with_alias call
site (where (msg.clone(), BlockNumberOrHash::PredefinedBlock(tag)) is passed) to
use BlockNumberOrHash::from_predefined(tag) so it matches the rest of the file
and uses the public API for Predefined variants.

1475-1483: EthCallV2 loop skips Predefined::Earliest and Predefined::Pending coverage.

The loop tests only [Latest, Safe, Finalized]. The sibling EthCall test at lines 1464–1473 covers only Latest. Given the PR's goal of backporting safe/finalized support, adding Pending to the V2 loop (with an appropriate PassWithQuasiIdenticalError policy) would match coverage parity with other V2 methods (e.g., EthGetBalanceV2 at lines 1908–1933 which tests all five predefined tags).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs` around lines 1475 - 1483,
The EthCallV2 loop currently iterates only over [Predefined::Latest,
Predefined::Safe, Predefined::Finalized], skipping Predefined::Earliest and
Predefined::Pending; update the loop to include both Predefined::Earliest and
Predefined::Pending so V2 test coverage matches other V2 methods, and for the
Pending case push the test using the PassWithQuasiIdenticalError policy (instead
of RpcTest::identity) to account for quasi-identical error behavior; locate the
loop building RpcTest entries for EthCallV2 and add entries for
Predefined::Earliest and Predefined::Pending (with Pending wrapped by the
PassWithQuasiIdenticalError helper) while keeping the existing use of
EthCallV2::request_with_alias and RpcTest constructors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/rpc/methods/eth.rs`:
- Around line 2722-2724: In EthGetTransactionByBlockNumberAndIndex, the V1
handler currently calls
BlockNumberOrHash::from(...).tipset_by_block_number_or_hash_v2(...), which uses
V2 finality rules; replace the call to use the V1 resolver by invoking
tipset_by_block_number_or_hash(...) instead (keep the same
ResolveNullTipset::TakeOlder and ctx/await usage) so the V1 path uses the V1
tipset resolution semantics.
- Around line 3896-3904: The handler always calls
tipset_by_block_number_or_hash_v2 which forces V2 semantics for all requests;
change it to pick the resolution function based on the incoming trace version by
checking the trace_types parameter (e.g., inspect trace_types or its
discriminator) and call tipset_by_block_number_or_hash (V1) for V1 requests and
tipset_by_block_number_or_hash_v2 (V2) for V2 requests, preserving the
appropriate ResolveNullTipset behavior for each path (e.g., keep
ResolveNullTipset::TakeOlder for V2 and the V1 semantics for V1).

---

Nitpick comments:
In `@src/rpc/methods/eth.rs`:
- Around line 307-373: Add doc comments for the newly public resolver APIs: impl
Predefined methods resolve_predefined_tipset, resolve_predefined_tipset_v1,
resolve_predefined_tipset_v2, and resolve_common_predefined_tipset (and any
other public functions in this impl) describing their purpose, parameters (ctx,
api_version or chain_store), behavior (how V1 vs V2 resolution works, env toggle
for F3 finality, and what each Predefined variant returns or errors), and the
returned Tipset/result. Keep comments brief, follow existing crate doc style
(///), include examples or panics/errors only if helpful, and ensure every
public function mentioned in this diff has a top-level rustdoc line.

In `@src/rpc/reflect/mod.rs`:
- Around line 367-371: Add a rustdoc comment for the new api_path helper in the
RpcMethodExt trait: above fn api_path(ext: &http::Extensions) ->
anyhow::Result<ApiPaths> add a short summary of what the helper does, document
the `ext` parameter (that it reads ApiPaths from http::Extensions), state the
return value (Ok(ApiPaths) or Err on missing/copy failure) and mention the error
context ("failed to resolve api path") so callers understand failure modes;
ensure the doc follows crate guidelines for public trait items and includes
examples or usage notes if helpful.

In `@src/rpc/request.rs`:
- Around line 35-42: Add doc comments for the public methods set_api_path and
with_api_path: document that set_api_path(&mut self, api_path: ApiPaths) sets
the request's API path to the provided ApiPaths value and mention mutates self,
and document that with_api_path(self, api_path: ApiPaths) -> Self is a
builder-style convenience that sets the API path and returns the modified
Request for chaining; include parameter descriptions for api_path and a brief
example or usage note if appropriate. Ensure the comments are placed immediately
above the function signatures for set_api_path and with_api_path and follow the
crate's doc-comment style (///).

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs`:
- Around line 1475-1483: Replace the direct construction
BlockNumberOrHash::PredefinedBlock(tag) with the public factory
BlockNumberOrHash::from_predefined(tag) inside the loop that builds
RpcTest::identity entries; update the EthCallV2::request_with_alias call site
(where (msg.clone(), BlockNumberOrHash::PredefinedBlock(tag)) is passed) to use
BlockNumberOrHash::from_predefined(tag) so it matches the rest of the file and
uses the public API for Predefined variants.
- Around line 1475-1483: The EthCallV2 loop currently iterates only over
[Predefined::Latest, Predefined::Safe, Predefined::Finalized], skipping
Predefined::Earliest and Predefined::Pending; update the loop to include both
Predefined::Earliest and Predefined::Pending so V2 test coverage matches other
V2 methods, and for the Pending case push the test using the
PassWithQuasiIdenticalError policy (instead of RpcTest::identity) to account for
quasi-identical error behavior; locate the loop building RpcTest entries for
EthCallV2 and add entries for Predefined::Earliest and Predefined::Pending (with
Pending wrapped by the PassWithQuasiIdenticalError helper) while keeping the
existing use of EthCallV2::request_with_alias and RpcTest constructors.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e9eb08 and 99d76eb.

📒 Files selected for processing (7)
  • src/rpc/client.rs
  • src/rpc/methods/eth.rs
  • src/rpc/mod.rs
  • src/rpc/reflect/mod.rs
  • src/rpc/request.rs
  • src/tool/subcommands/api_cmd.rs
  • src/tool/subcommands/api_cmd/api_compare_tests.rs
💤 Files with no reviewable changes (1)
  • src/rpc/mod.rs

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: 2

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)

1121-1131: ⚠️ Potential issue | 🟠 Major

Missing EC fallback when F3 tipset load fails can break finalized/safe RPCs.

At Line 1123, a load_required_tipset failure is returned directly. That differs from ChainGetFinalizedTipset::handle, which falls back to EC finality on F3 failures. This can surface avoidable errors for finalized/safe lookups even when EC fallback is available.

Suggested fix
-        let ts = ctx
-            .chain_index()
-            .load_required_tipset(&f3_finalized_head.key)
-            .map_err(|e| {
-                anyhow::anyhow!(
-                    "Failed to load F3 finalized tipset at epoch {} with key {}: {e}",
-                    f3_finalized_head.epoch,
-                    f3_finalized_head.key,
-                )
-            })?;
-        Ok(ts)
+        match ctx.chain_index().load_required_tipset(&f3_finalized_head.key) {
+            Ok(ts) => Ok(ts),
+            Err(e) => {
+                tracing::warn!(
+                    "Failed to load F3 finalized tipset at epoch {} with key {}: {e}; \
+                     falling back to EC finality",
+                    f3_finalized_head.epoch,
+                    f3_finalized_head.key,
+                );
+                Self::get_ec_finalized_tipset(ctx)
+            }
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/chain.rs` around lines 1121 - 1131, The current direct
failure return from
ctx.chain_index().load_required_tipset(&f3_finalized_head.key) can abort
finalized/safe RPCs; instead, catch the error and invoke the same EC-finality
fallback used by ChainGetFinalizedTipset::handle: on load_required_tipset error
for f3_finalized_head, call the ChainGetFinalizedTipset::handle's EC-path (the
code that computes/loads the EC-finalized tipset from ctx/chain_index) and
return that tipset if it succeeds; only propagate an error if both the primary
load_required_tipset and the EC fallback fail—also include contextual logging
when falling back for easier debugging.
src/rpc/methods/eth.rs (1)

299-373: ⚠️ Potential issue | 🟡 Minor

Public block-selector API additions need rustdoc.

Predefined, BlockNumberOrHash, and newly added public constructors/parser in this changed section are public API surface but undocumented.

As per coding guidelines "**/*.rs: Document all public functions and structs with doc comments".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth.rs` around lines 299 - 373, Add rustdoc comments to the
public API items introduced in this diff: document the enum Predefined, the
structs BlockNumber and BlockHash, the enum BlockNumberOrHash, and all public
constructors/parsers (from_predefined, from_block_number, from_block_hash,
from_block_number_object, from_block_hash_object, from_str) with brief
descriptions of purpose, behavior, parameters and return values; place /// doc
comments above each type and method (mention EIP-1898 where relevant for
from_block_number_object/from_block_hash_object and note require_canonical
semantics for BlockHash and from_block_hash_object) so the public block-selector
API is fully documented per guidelines.
🧹 Nitpick comments (2)
src/rpc/methods/eth/tipset_resolver.rs (1)

19-19: Avoid duplicating SAFE_HEIGHT_DISTANCE across RPC modules.

Line 19 defines the same constant already present in src/rpc/methods/chain.rs (Line 71). Keeping two copies risks silent behavioral drift between ETH and Chain safe-tipset logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth/tipset_resolver.rs` at line 19, Duplicate constant
SAFE_HEIGHT_DISTANCE should be removed from tipset_resolver.rs and the code
should reference the single source-of-truth constant defined in the chain module
instead; locate the SAFE_HEIGHT_DISTANCE symbol in tipset_resolver.rs, delete
its local definition, and update any usages to import or refer to the existing
SAFE_HEIGHT_DISTANCE from the chain module (e.g., use the chain module's
SAFE_HEIGHT_DISTANCE constant) so ETH and Chain logic share the same value.
src/tool/subcommands/api_cmd/api_compare_tests.rs (1)

1600-1708: Factor repeated ETH request matrices into a helper to reduce drift.

This block duplicates near-identical EthGetBalance cases across explicit-path and default-path variants. A small helper that accepts (api_path, block_selector) would make future updates much safer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs` around lines 1600 - 1708,
Refactor the repeated EthGetBalance test entries by extracting a helper (e.g.,
build_eth_get_balance_tests) that takes parameters (api_path: Option<ApiPaths>,
block_selector: BlockNumberOrHash, policy: Option<PolicyOnRejected>, test_kind:
enum {Identity,Basic}) and returns the appropriate RpcTest(s) using
EthGetBalance::request, so you replace the duplicated RpcTest::identity(...) and
RpcTest::basic(...) calls with calls to this helper; ensure it handles both
with_api_path(ApiPaths::V1) and default-path variants, the various
BlockNumberOrHash constructors (from_block_number, from_block_number_object,
from_block_hash_object, from_predefined, etc.), and preserves policy_on_rejected
where used.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/rpc/methods/eth.rs`:
- Around line 3029-3034: The FilecoinAddressToEthAddress handler currently
defaults block_param to Predefined::Finalized which differs from EthTraceCall's
Predefined::Latest and affects lookup_required_id() state visibility; either
change the default to Predefined::Latest (modify the block_param unwrap_or in
FilecoinAddressToEthAddress to use Predefined::Latest and keep
resolver.tipset_by_block_number_or_hash calls intact) to match other RPCs like
EthTraceCall, or if Finalized is deliberate, add a concise comment above the
FilecoinAddressToEthAddress logic documenting why lookup_required_id() requires
a finalized tipset (mention lookup_required_id() and
resolver.tipset_by_block_number_or_hash) so the design rationale is clear.

In `@src/rpc/methods/eth/tipset_resolver.rs`:
- Around line 21-35: Add Rustdoc comments for the public TipsetResolver API:
document the TipsetResolver struct and its public methods (notably
TipsetResolver::new) describing purpose, parameters (ctx: &Ctx<DB>, api_version:
ApiPaths), lifetime and trait bounds (DB: Blockstore + Send + Sync + 'static),
and example usage or notes about thread-safety/ownership where relevant; ensure
you update docs for any other public functions in the same file (lines ~37-139)
so all exported items have doc comments explaining behavior, inputs, outputs,
and panics/errors.

---

Outside diff comments:
In `@src/rpc/methods/chain.rs`:
- Around line 1121-1131: The current direct failure return from
ctx.chain_index().load_required_tipset(&f3_finalized_head.key) can abort
finalized/safe RPCs; instead, catch the error and invoke the same EC-finality
fallback used by ChainGetFinalizedTipset::handle: on load_required_tipset error
for f3_finalized_head, call the ChainGetFinalizedTipset::handle's EC-path (the
code that computes/loads the EC-finalized tipset from ctx/chain_index) and
return that tipset if it succeeds; only propagate an error if both the primary
load_required_tipset and the EC fallback fail—also include contextual logging
when falling back for easier debugging.

In `@src/rpc/methods/eth.rs`:
- Around line 299-373: Add rustdoc comments to the public API items introduced
in this diff: document the enum Predefined, the structs BlockNumber and
BlockHash, the enum BlockNumberOrHash, and all public constructors/parsers
(from_predefined, from_block_number, from_block_hash, from_block_number_object,
from_block_hash_object, from_str) with brief descriptions of purpose, behavior,
parameters and return values; place /// doc comments above each type and method
(mention EIP-1898 where relevant for
from_block_number_object/from_block_hash_object and note require_canonical
semantics for BlockHash and from_block_hash_object) so the public block-selector
API is fully documented per guidelines.

---

Nitpick comments:
In `@src/rpc/methods/eth/tipset_resolver.rs`:
- Line 19: Duplicate constant SAFE_HEIGHT_DISTANCE should be removed from
tipset_resolver.rs and the code should reference the single source-of-truth
constant defined in the chain module instead; locate the SAFE_HEIGHT_DISTANCE
symbol in tipset_resolver.rs, delete its local definition, and update any usages
to import or refer to the existing SAFE_HEIGHT_DISTANCE from the chain module
(e.g., use the chain module's SAFE_HEIGHT_DISTANCE constant) so ETH and Chain
logic share the same value.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs`:
- Around line 1600-1708: Refactor the repeated EthGetBalance test entries by
extracting a helper (e.g., build_eth_get_balance_tests) that takes parameters
(api_path: Option<ApiPaths>, block_selector: BlockNumberOrHash, policy:
Option<PolicyOnRejected>, test_kind: enum {Identity,Basic}) and returns the
appropriate RpcTest(s) using EthGetBalance::request, so you replace the
duplicated RpcTest::identity(...) and RpcTest::basic(...) calls with calls to
this helper; ensure it handles both with_api_path(ApiPaths::V1) and default-path
variants, the various BlockNumberOrHash constructors (from_block_number,
from_block_number_object, from_block_hash_object, from_predefined, etc.), and
preserves policy_on_rejected where used.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99d76eb and 71e3403.

📒 Files selected for processing (5)
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/tipset_resolver.rs
  • src/rpc/mod.rs
  • src/tool/subcommands/api_cmd/api_compare_tests.rs
💤 Files with no reviewable changes (1)
  • src/rpc/mod.rs

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

CodeRabbit commands

These commands are invoked using PR/Issue comments.

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai evaluate custom pre-merge check --instructions <custom-checks-instructions> --name <custom-checks-title> [--mode <error|warning>] to validate the custom pre-merge checks instructions. Defaults to error when --mode is omitted.
    • @coderabbitai ignore pre-merge checks to override pre-merge checks and get an approval on PR.
    • @coderabbitai run pre-merge checks to run pre-merge checks on the pull request.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai generate configuration to create a PR that adds the current resolved configuration as .coderabbit.yaml (or show it if already present).
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit configuration file (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, support, documentation and community

  • Visit our status page to check the current availability of CodeRabbit.
  • Create a ticket on our support page for assistance with any issues or questions.
  • Visit our documentation site for detailed information on how to use CodeRabbit.
  • Join our Discord community to connect with other users and get help from the community.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

✅ Actions performed

Initiated docstring generation; will generate only if new commits exist.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #6649

coderabbitai bot added a commit that referenced this pull request Feb 25, 2026
Docstrings generation was requested by @hanabi1224.

* #6644 (comment)

The following files were modified:

* `src/rpc/client.rs`
* `src/rpc/methods/chain.rs`
* `src/rpc/methods/eth.rs`
* `src/rpc/methods/eth/tipset_resolver.rs`
* `src/rpc/methods/eth/types.rs`
* `src/rpc/reflect/mod.rs`
* `src/rpc/request.rs`
* `src/tool/subcommands/api_cmd.rs`
* `src/tool/subcommands/api_cmd/api_compare_tests.rs`
* `src/tool/subcommands/api_cmd/test_snapshot.rs`
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: 2

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)

1090-1142: ⚠️ Potential issue | 🟡 Minor

Document the changed public tipset helpers.

The changed public methods (get_latest_safe_tipset, get_latest_finalized_tipset, get_ec_finalized_tipset) need rustdoc describing fallback behavior and guarantees.

As per coding guidelines, "Document all public functions and structs with doc comments".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/chain.rs` around lines 1090 - 1142, Add rustdoc comments
(///) above the three public functions get_latest_safe_tipset,
get_latest_finalized_tipset, and get_ec_finalized_tipset that explain their
purpose, inputs and return value, the fallback behavior
(get_latest_finalized_tipset falls back to get_ec_finalized_tipset when F3 data
is unavailable or too old; get_latest_safe_tipset returns the F3-finalized
tipset when newer than safe height otherwise returns a tipset at safe_height),
the guarantees (what "finalized" or "safe" means relative to chain_finality and
SAFE_HEIGHT_DISTANCE), and possible error conditions (when loading tipsets
fails). Keep each doc comment short, mention ResolveNullTipset::TakeOlder
behavior where relevant, and ensure they follow rustdoc style and cover usage
expectations and error semantics.
src/tool/subcommands/api_cmd/api_compare_tests.rs (1)

1475-1483: ⚠️ Potential issue | 🟡 Minor

Add Safe/Finalized block tag tests for EthCall V1 to match V2 coverage.

The EthCall V1 handler uses the same TipsetResolver that resolves Safe and Finalized predefined blocks, yet the test (lines 1467–1470) only exercises it with Latest. Expand the V1 test loop to cover [Latest, Safe, Finalized] like V2 (lines 1475–1483) to ensure parity in test coverage for all supported block tag parameters.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs` around lines 1475 - 1483,
Update the EthCall V1 test loop to include Predefined::Safe and
Predefined::Finalized in addition to Predefined::Latest so it matches the V2
coverage; specifically, modify the loop that pushes RpcTest::identity entries
created from EthCall::request_with_alias (the V1 counterpart to
EthCallV2::request_with_alias) to iterate over [Predefined::Latest,
Predefined::Safe, Predefined::Finalized] and push a test for each tag using the
same (msg.clone(), BlockNumberOrHash::PredefinedBlock(tag)) and use_alias
parameters.
♻️ Duplicate comments (1)
src/rpc/methods/eth/tipset_resolver.rs (1)

7-23: ⚠️ Potential issue | 🟡 Minor

Add rustdoc for the public TipsetResolver API.

TipsetResolver and its public methods are still undocumented. Please add rustdoc for purpose, parameters, return/error behavior, and V1/V2/F3 semantics.

As per coding guidelines, "Document all public functions and structs with doc comments".

Also applies to: 23-55, 105-124

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth/tipset_resolver.rs` around lines 7 - 23, Add rustdoc
comments for the public TipsetResolver API: document the struct TipsetResolver
(purpose and ownership of ctx and api_version), the constructor new(ctx:
&Ctx<DB>, api_version: ApiPaths) (describe parameters and lifetime), and the
public method tipset_by_block_number_or_hash (describe parameters, return type,
error behavior, async semantics, and explicitly state how behavior differs for
ApiPaths V1/V2/F3). Ensure the docs follow Rustdoc style, include examples or
panics/errors if relevant, and apply the same documentation pattern to the other
public methods referenced in the file.
🧹 Nitpick comments (4)
src/rpc/methods/eth.rs (1)

1505-1508: Consider extracting a small helper for repeated resolver calls.

The same resolver-init + tipset-lookup sequence is repeated across many handlers; a helper would reduce boilerplate and drift risk.

♻️ Example extraction
+async fn resolve_tipset_for_request<DB: Blockstore + Send + Sync + 'static>(
+    ctx: &Ctx<DB>,
+    api_path: ApiPaths,
+    block_param: impl Into<BlockNumberOrHash>,
+    resolve: ResolveNullTipset,
+) -> anyhow::Result<Tipset> {
+    TipsetResolver::new(ctx, api_path)
+        .tipset_by_block_number_or_hash(block_param, resolve)
+        .await
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth.rs` around lines 1505 - 1508, Extract a small helper
(e.g., resolve_tipset) to consolidate the repeated resolver-init and lookup:
have it accept the context (&ctx), extension (ext) or api path inputs (or
directly call Self::api_path inside), the block_param, and a ResolveNullTipset
policy, then inside call TipsetResolver::new(&ctx, Self::api_path(ext)?) and
await tipset_by_block_number_or_hash(block_param, policy). Replace occurrences
of TipsetResolver::new + tipset_by_block_number_or_hash with calls to this
helper to remove boilerplate and centralize error handling.
src/rpc/methods/eth/tipset_resolver.rs (1)

115-123: Avoid duplicating EC-finality computation.

get_ec_finalized_tipset duplicates logic already present in ChainGetTipSetV2::get_ec_finalized_tipset, which increases drift risk.

♻️ Suggested deduplication
-    pub fn get_ec_finalized_tipset(&self) -> anyhow::Result<Tipset> {
-        let head = self.ctx.chain_store().heaviest_tipset();
-        let ec_finality_epoch =
-            (head.epoch() - self.ctx.chain_config().policy.chain_finality).max(0);
-        Ok(self.ctx.chain_index().tipset_by_height(
-            ec_finality_epoch,
-            head,
-            ResolveNullTipset::TakeOlder,
-        )?)
-    }
+    pub fn get_ec_finalized_tipset(&self) -> anyhow::Result<Tipset> {
+        ChainGetTipSetV2::get_ec_finalized_tipset(self.ctx)
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth/tipset_resolver.rs` around lines 115 - 123, The
get_ec_finalized_tipset function duplicates EC-finality computation; instead
delegate to the existing implementation or shared helper in ChainGetTipSetV2 to
avoid drift. Replace the manual computation in get_ec_finalized_tipset (which
uses self.ctx.chain_store(), self.ctx.chain_config().policy.chain_finality, and
self.ctx.chain_index().tipset_by_height) with a call to
ChainGetTipSetV2::get_ec_finalized_tipset (or extract the epoch calculation into
a new private helper used by both) so both callers use the same logic and return
path.
src/tool/subcommands/api_cmd/api_compare_tests.rs (2)

1868-1873: Inconsistent BlockNumberOrHash construction style in EthGetStorageAt V1 test.

Line 1872 uses the raw variant BlockNumberOrHash::BlockNumber(EthInt64(shared_tipset.epoch())) directly, while the analogous V2 test (Line 1898) and the majority of other tests use the from_block_number helper. This inconsistency is harmless but reduces readability.

♻️ Proposed fix
-        RpcTest::identity(EthGetStorageAt::request((
-            // https://filfox.info/en/address/f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq
-            EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?,
-            EthBytes(vec![0xa]),
-            BlockNumberOrHash::BlockNumber(EthInt64(shared_tipset.epoch())),
-        ))?),
+        RpcTest::identity(EthGetStorageAt::request((
+            // https://filfox.info/en/address/f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq
+            EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?,
+            EthBytes(vec![0xa]),
+            BlockNumberOrHash::from_block_number(shared_tipset.epoch()),
+        ))?),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs` around lines 1868 - 1873,
Replace the direct construction
BlockNumberOrHash::BlockNumber(EthInt64(shared_tipset.epoch())) used in the
RpcTest::identity(EthGetStorageAt::request(...)) call with the helper
constructor
BlockNumberOrHash::from_block_number(EthInt64(shared_tipset.epoch())) so the V1
test matches the V2 style and other tests for consistency and readability.

2430-2438: Add assertion or saturating fallback for snapshot depth.

SAFE_EPOCH_DELAY_FOR_TESTING (20 epochs) equals only 10 minutes of chain history at 30-second block times. If a CI snapshot contains fewer than 20 finalized epochs, shared_tipset will end up shallower than intended, potentially undermining the finality guarantee the code attempts to ensure. Consider asserting minimum snapshot depth or using a saturating subtraction to handle undersized snapshots gracefully.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs` around lines 2430 - 2438,
The code currently assumes SAFE_EPOCH_DELAY_FOR_TESTING epochs exist when
calling store.heaviest_tipset()?.chain(&store).take(SAFE_EPOCH_DELAY_FOR_TESTING
as usize).last(); add a guard so the snapshot depth is checked and handled:
compute the actual chain length from heaviest_tipset.chain(&store) (e.g., count
or collect into an iterator), then use a saturating/minimum value for the take
size (let take_depth = min(actual_depth, SAFE_EPOCH_DELAY_FOR_TESTING as usize))
before calling .take(take_depth).last(), or alternatively assert that
actual_depth >= SAFE_EPOCH_DELAY_FOR_TESTING and emit a clear error; reference
SAFE_EPOCH_DELAY_FOR_TESTING, shared_tipset, heaviest_tipset, and chain(&store)
when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/rpc/methods/eth.rs`:
- Around line 340-344: Add a Rust doc comment for the public constructor
BlockNumberOrHash::from_predefined explaining that it constructs a
BlockNumberOrHash from a Predefined tag (e.g., "latest", "earliest", "pending")
and returns the PredefinedBlock variant; include a short example of typical
usage and note the parameter type (Predefined) and the returned type
(BlockNumberOrHash) so consumers understand purpose and usage of the function.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs`:
- Around line 2142-2149: Add tests exercising the Predefined::Safe and
Predefined::Finalized variants for both EthTraceReplayBlockTransactions and
EthTraceReplayBlockTransactionsV2: for each of the two RPC handlers
(EthTraceReplayBlockTransactions and EthTraceReplayBlockTransactionsV2) add
RpcTest::identity requests that pass BlockNumberOrHash constructed with
Predefined::Safe and Predefined::Finalized (mirroring the existing epoch-based
calls), so the suite covers TipsetResolver-supported tags the same way
EthTraceBlock/EthTraceBlockV2 do.

---

Outside diff comments:
In `@src/rpc/methods/chain.rs`:
- Around line 1090-1142: Add rustdoc comments (///) above the three public
functions get_latest_safe_tipset, get_latest_finalized_tipset, and
get_ec_finalized_tipset that explain their purpose, inputs and return value, the
fallback behavior (get_latest_finalized_tipset falls back to
get_ec_finalized_tipset when F3 data is unavailable or too old;
get_latest_safe_tipset returns the F3-finalized tipset when newer than safe
height otherwise returns a tipset at safe_height), the guarantees (what
"finalized" or "safe" means relative to chain_finality and
SAFE_HEIGHT_DISTANCE), and possible error conditions (when loading tipsets
fails). Keep each doc comment short, mention ResolveNullTipset::TakeOlder
behavior where relevant, and ensure they follow rustdoc style and cover usage
expectations and error semantics.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs`:
- Around line 1475-1483: Update the EthCall V1 test loop to include
Predefined::Safe and Predefined::Finalized in addition to Predefined::Latest so
it matches the V2 coverage; specifically, modify the loop that pushes
RpcTest::identity entries created from EthCall::request_with_alias (the V1
counterpart to EthCallV2::request_with_alias) to iterate over
[Predefined::Latest, Predefined::Safe, Predefined::Finalized] and push a test
for each tag using the same (msg.clone(),
BlockNumberOrHash::PredefinedBlock(tag)) and use_alias parameters.

---

Duplicate comments:
In `@src/rpc/methods/eth/tipset_resolver.rs`:
- Around line 7-23: Add rustdoc comments for the public TipsetResolver API:
document the struct TipsetResolver (purpose and ownership of ctx and
api_version), the constructor new(ctx: &Ctx<DB>, api_version: ApiPaths)
(describe parameters and lifetime), and the public method
tipset_by_block_number_or_hash (describe parameters, return type, error
behavior, async semantics, and explicitly state how behavior differs for
ApiPaths V1/V2/F3). Ensure the docs follow Rustdoc style, include examples or
panics/errors if relevant, and apply the same documentation pattern to the other
public methods referenced in the file.

---

Nitpick comments:
In `@src/rpc/methods/eth.rs`:
- Around line 1505-1508: Extract a small helper (e.g., resolve_tipset) to
consolidate the repeated resolver-init and lookup: have it accept the context
(&ctx), extension (ext) or api path inputs (or directly call Self::api_path
inside), the block_param, and a ResolveNullTipset policy, then inside call
TipsetResolver::new(&ctx, Self::api_path(ext)?) and await
tipset_by_block_number_or_hash(block_param, policy). Replace occurrences of
TipsetResolver::new + tipset_by_block_number_or_hash with calls to this helper
to remove boilerplate and centralize error handling.

In `@src/rpc/methods/eth/tipset_resolver.rs`:
- Around line 115-123: The get_ec_finalized_tipset function duplicates
EC-finality computation; instead delegate to the existing implementation or
shared helper in ChainGetTipSetV2 to avoid drift. Replace the manual computation
in get_ec_finalized_tipset (which uses self.ctx.chain_store(),
self.ctx.chain_config().policy.chain_finality, and
self.ctx.chain_index().tipset_by_height) with a call to
ChainGetTipSetV2::get_ec_finalized_tipset (or extract the epoch calculation into
a new private helper used by both) so both callers use the same logic and return
path.

In `@src/tool/subcommands/api_cmd/api_compare_tests.rs`:
- Around line 1868-1873: Replace the direct construction
BlockNumberOrHash::BlockNumber(EthInt64(shared_tipset.epoch())) used in the
RpcTest::identity(EthGetStorageAt::request(...)) call with the helper
constructor
BlockNumberOrHash::from_block_number(EthInt64(shared_tipset.epoch())) so the V1
test matches the V2 style and other tests for consistency and readability.
- Around line 2430-2438: The code currently assumes SAFE_EPOCH_DELAY_FOR_TESTING
epochs exist when calling
store.heaviest_tipset()?.chain(&store).take(SAFE_EPOCH_DELAY_FOR_TESTING as
usize).last(); add a guard so the snapshot depth is checked and handled: compute
the actual chain length from heaviest_tipset.chain(&store) (e.g., count or
collect into an iterator), then use a saturating/minimum value for the take size
(let take_depth = min(actual_depth, SAFE_EPOCH_DELAY_FOR_TESTING as usize))
before calling .take(take_depth).last(), or alternatively assert that
actual_depth >= SAFE_EPOCH_DELAY_FOR_TESTING and emit a clear error; reference
SAFE_EPOCH_DELAY_FOR_TESTING, shared_tipset, heaviest_tipset, and chain(&store)
when making the change.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 71e3403 and af17113.

⛔ Files ignored due to path filters (3)
  • src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap is excluded by !**/*.snap
  • src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap is excluded by !**/*.snap
  • src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap is excluded by !**/*.snap
📒 Files selected for processing (5)
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/tipset_resolver.rs
  • src/rpc/mod.rs
  • src/tool/subcommands/api_cmd/api_compare_tests.rs
💤 Files with no reviewable changes (1)
  • src/rpc/mod.rs

@hanabi1224
Copy link
Contributor Author

@akaladarshi

  1. Consider introducing a tipsetResolve here which can help us resolve all the enum variants based on the env and params.

Done.

  1. We could move the F3/EC related tag resolution to the tipsetResolver, keeping ChainGetTipSetV2 focused purely on API responsibilities.

The current logic under ChainGetTipSetV2 is not for eth tipset resolution. Please note that the tag used in ChainGetTipSetV2 is different from the tag used in EthTipsetResolver, e.g. Latest in ChainGetTipSetV2 means Pending in EthTipsetResolver

  1. It would be good to structure this in a way that avoids unnecessary refactors once ETH_V1_DISABLE_F3_FINALITY_RESOLUTION_ENV_KEY is removed: filecoin-project/lotus#13315

Should be easy with the new TipsetResolver

  1. EthGetTransactionByBlockNumberAndIndex still uses the tipset_by_block_number_or_hash_v2.

V2 enums are now merged with V1

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.

Support safe and finalized in V1 APIs

2 participants