Skip to content

Conversation

@akaladarshi
Copy link
Collaborator

@akaladarshi akaladarshi commented Dec 16, 2025

Summary of changes

Changes introduced in this pull request:

  • Convert the limit param into a correct epoch value while searching for the message

Reference issue to close (if applicable)

Closes #6346

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

  • New Features

    • Exposed a lookback constant for external use
    • Added public descriptive text for a state query method
  • Refactor

    • Reworked message search to use an explicit minimum-epoch boundary and non-optional replacement handling for clearer, more direct search semantics
  • Documentation

    • Updated in-code descriptions to reflect the new boundary semantics and options

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

@akaladarshi akaladarshi requested a review from a team as a code owner December 16, 2025 11:09
@akaladarshi akaladarshi requested review from LesnyRumcajs and hanabi1224 and removed request for a team December 16, 2025 11:09
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

Walkthrough

Refactors state-manager look-back search to use an explicit minimum-epoch boundary and non-optional allow_replaced flag; exposes LOOKBACK_NO_LIMIT as a public constant; and adds a DESCRIPTION associated constant to the StateSearchMsgLimited RPC method implementation.

Changes

Cohort / File(s) Summary
RPC metadata
src/rpc/methods/state.rs
Added a public associated constant DESCRIPTION to the impl RpcMethod<2> for StateSearchMsgLimited and removed the prior inline documentation comment.
RPC constants
src/rpc/mod.rs
Changed visibility of LOOKBACK_NO_LIMIT from private to pub (pub const LOOKBACK_NO_LIMIT: ChainEpoch = -1;).
Lookback logic refactor
src/state_manager/mod.rs
Replaced look_back_limit: Option<i64> and allow_replaced: Option<bool> with min_epoch: ChainEpoch and allow_replaced: bool; changed search_back_for_message and check_search signatures and loop termination to use min_epoch as the stop boundary (no unwraps), computing the boundary from prior look-back semantics before calling into the new functions. Updated inline docs accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review search_back_for_message and check_search signature changes and call sites for correct boundary computation and propagation of allow_replaced.
  • Verify conversion from previous optional look-back semantics to concrete min_epoch (including handling of LOOKBACK_NO_LIMIT).
  • Confirm no remaining calls expect the old optional parameter types and that behaviour across epoch-edge cases (limit 0, limit equal to current epoch, unlimited) matches intent.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes an out-of-scope change: exposing LOOKBACK_NO_LIMIT as public and adding DESCRIPTION constant to StateSearchMsgLimited, which are not required by issue #6346. Remove the public visibility change to LOOKBACK_NO_LIMIT and the DESCRIPTION constant addition, as these are not part of the core fix for the look-back limit bug.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective: converting look-back limit to actual epoch before searching for messages, which directly addresses the core bug fix.
Linked Issues check ✅ Passed The code changes implement the core requirement from issue #6346: converting relative look-back limit to absolute epoch boundary in search_back_for_message and check_search functions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch akaladarshi/fix-search-msg-bug

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/rpc/mod.rs (1)

44-44: Public LOOKBACK_NO_LIMIT is fine; consider documenting sentinel semantics.

Exposing this constant is safe and matches its use in StateManager. You might add a short doc comment explaining that -1 means “no lookback limit” for RPCs that accept a limit, so its meaning is clear in generated docs/OpenRPC.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2fc9d2 and e6e204c.

📒 Files selected for processing (3)
  • src/rpc/methods/state.rs (1 hunks)
  • src/rpc/mod.rs (1 hunks)
  • src/state_manager/mod.rs (4 hunks)
⏰ 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: Build MacOS
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: Coverage
  • GitHub Check: Build Ubuntu
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: All lint checks
  • GitHub Check: tests-release
  • GitHub Check: tests
🔇 Additional comments (2)
src/rpc/methods/state.rs (1)

1277-1279: Good addition of DESCRIPTION for StateSearchMsgLimited.

The description matches the Lotus docs wording and will improve OpenRPC/spec discoverability without changing behavior.

src/state_manager/mod.rs (1)

30-30: Relative lookback conversion is correctly implemented; consider a small helper for testability and verify boundary semantics against Lotus.

The new mapping correctly interprets limit as "epochs back from the current head," with LOOKBACK_NO_LIMIT == -1 disabling the bound:

let look_back_limit_in_epoch = look_back_limit.and_then(|limit| {
    if limit > LOOKBACK_NO_LIMIT && limit < current.epoch() {
        Some(current.epoch().saturating_sub(limit))
    } else {
        None // no limit → search back to genesis
    }
});

The loop condition while current.epoch() > look_back_limit_in_epoch.unwrap_or_default() uses an exclusive bound, searching epochs strictly less than limit distance from the head. The RPC docstring says "looks back up to limit epochs," which aligns with this exclusive semantics; however, confirm this matches Lotus' Go implementation to ensure correctness.

Edge cases are handled correctly:

  • None / limit <= -1 / limit >= current.epoch()look_back_limit_in_epoch == None → search to genesis.
  • limit == 0 → bound at current.epoch() so the loop doesn't run; only the direct tipset_executed_message check applies.

For readability and easier unit testing of boundary edge cases, extract this conversion into a small helper:

fn to_epoch_bound(head_epoch: ChainEpoch, limit: Option<i64>) -> Option<ChainEpoch> { ... }

Minor layering note: importing LOOKBACK_NO_LIMIT from crate::rpc couples core state logic to the RPC layer. If reused broadly, consider a shared lower-level constants module.

@codecov
Copy link

codecov bot commented Dec 16, 2025

Codecov Report

❌ Patch coverage is 0% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 40.39%. Comparing base (e2fc9d2) to head (4316d77).

Files with missing lines Patch % Lines
src/state_manager/mod.rs 0.00% 10 Missing ⚠️
Additional details and impacted files
Files with missing lines Coverage Δ
src/rpc/methods/state.rs 0.00% <ø> (ø)
src/rpc/mod.rs 15.10% <ø> (ø)
src/state_manager/mod.rs 5.97% <0.00%> (-0.03%) ⬇️

... 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 e2fc9d2...4316d77. Read the comment docs.

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

@akaladarshi akaladarshi added the RPC requires calibnet RPC checks to run on CI label Dec 16, 2025
@LesnyRumcajs
Copy link
Member

no green checkmark, no review ❌

@akaladarshi akaladarshi force-pushed the akaladarshi/fix-search-msg-bug branch from e6e204c to 4316d77 Compare December 16, 2025 12:44
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6e204c and 4316d77.

📒 Files selected for processing (3)
  • src/rpc/methods/state.rs (1 hunks)
  • src/rpc/mod.rs (1 hunks)
  • src/state_manager/mod.rs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/rpc/mod.rs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-25T13:35:24.230Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5969
File: src/tool/subcommands/snapshot_cmd.rs:412-412
Timestamp: 2025-08-25T13:35:24.230Z
Learning: In src/tool/subcommands/snapshot_cmd.rs, the +1 in `last_epoch = ts.epoch() - epochs as i64 + 1` fixes an off-by-1 bug where specifying --check-stateroots=N would validate N+1 epochs instead of N epochs, causing out-of-bounds errors when the snapshot contains only N recent state roots.

Applied to files:

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

1277-1279: Documentation improvement looks good.

The DESCRIPTION constant correctly explains that the method "looks back up to limit epochs" which aligns with the fix that interprets limit as a relative look-back distance rather than an absolute epoch.

src/state_manager/mod.rs (2)

1046-1093: LGTM - Clean refactoring to use explicit epoch boundary.

The refactored signature accepting min_epoch: ChainEpoch instead of look_back_limit: Option<i64> improves clarity by separating concerns: conversion logic is in search_back_for_message, and this function focuses on the actual search using a concrete epoch boundary.

The non-optional allow_replaced parameter eliminates unnecessary unwrapping, making the code safer.


1095-1105: Excellent documentation!

The comprehensive documentation clearly explains all cases for the look_back_limit parameter, including the special LOOKBACK_NO_LIMIT (-1) value. This significantly improves code maintainability and makes the intended behavior explicit.

Comment on lines 1106 to 1128
fn search_back_for_message(
&self,
current: Tipset,
message: &ChainMessage,
look_back_limit: Option<i64>,
allow_replaced: Option<bool>,
) -> Result<Option<(Tipset, Receipt)>, Error> {
self.check_search(current, message, look_back_limit, allow_replaced)
let current_epoch = current.epoch();
let allow_replaced = allow_replaced.unwrap_or(true);

// Calculate the minimum epoch (inclusive lower bound) for the search.
let min_epoch = match look_back_limit {
// No search: limit = 0 means search 0 epochs
Some(0) => return Ok(None),
// Limited search: calculate the inclusive lower bound
// For ex: limit = 5 at epoch 1000: min_epoch = 996, searches [996, 1000] = 5 epochs
Some(limit) if limit > 0 && limit < current_epoch => current_epoch - limit + 1,
// Search all the way to genesis (epoch 0)
_ => 0,
};

self.check_search(current, message, min_epoch, allow_replaced)
}
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 16, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the file and check if it exists
find . -name "mod.rs" -path "*/state_manager/*" 2>/dev/null | head -20

Repository: ChainSafe/forest

Length of output: 86


🏁 Script executed:

# Read the specific lines mentioned in the review
sed -n '1106,1128p' ./src/state_manager/mod.rs

# Also check a bit before and after for context
echo "=== CONTEXT BEFORE ==="
sed -n '1090,1105p' ./src/state_manager/mod.rs

echo "=== TARGET LINES ==="
sed -n '1106,1128p' ./src/state_manager/mod.rs

echo "=== CONTEXT AFTER ==="
sed -n '1129,1140p' ./src/state_manager/mod.rs

Repository: ChainSafe/forest

Length of output: 3385


🏁 Script executed:

# Search for tests related to search_back_for_message
grep -r "search_back_for_message" . --include="*.rs" -n

# Search for any tests in the state_manager module
echo "=== Looking for test modules in state_manager ==="
grep -n "mod test\|#\[test\|#\[cfg(test)" ./src/state_manager/mod.rs | head -20

Repository: ChainSafe/forest

Length of output: 542


🏁 Script executed:

# Check for issue #6346 references
grep -r "6346" . --include="*.rs" --include="*.md" --include="*.txt"

# Search for any recent changes or comments about look_back_limit
grep -n "look_back_limit\|look back\|LOOKBACK_NO_LIMIT" ./src/state_manager/mod.rs

Repository: ChainSafe/forest

Length of output: 5509


🏁 Script executed:

# Check for test file
find ./src/state_manager -name "tests.rs" -o -name "test.rs" 2>/dev/null

# List what's in the tests module
ls -la ./src/state_manager/

Repository: ChainSafe/forest

Length of output: 642


🏁 Script executed:

# Search for tests that might test search_back_for_message or related functionality
grep -A 10 "search_back_for_message\|look_back_limit" ./src/state_manager/tests.rs 2>/dev/null | head -50

# If tests.rs exists, check its size and structure
wc -l ./src/state_manager/tests.rs 2>/dev/null
grep "#\[test\]" ./src/state_manager/tests.rs | head -20

Repository: ChainSafe/forest

Length of output: 172


🏁 Script executed:

# Check the usage of search_back_for_message in the search_msg method
sed -n '1275,1295p' ./src/state_manager/mod.rs

Repository: ChainSafe/forest

Length of output: 976


🏁 Script executed:

# Search for tests related to search_back_for_message or look_back_limit in tests.rs
grep -n "search_back_for_message\|search_for_message\|look_back_limit" ./src/state_manager/tests.rs

# Also check the test function names
grep -n "fn test_\|#\[test\]" ./src/state_manager/tests.rs | head -40

Repository: ChainSafe/forest

Length of output: 796


🏁 Script executed:

# Check if there are any tests that cover the epoch calculation logic
grep -B 5 -A 20 "epoch\|limit" ./src/state_manager/tests.rs | head -80

Repository: ChainSafe/forest

Length of output: 2370


🏁 Script executed:

# Verify the condition logic in the code by looking at the exact match pattern
sed -n '1117,1125p' ./src/state_manager/mod.rs

# Also check if there's documentation about the fix
git log --oneline --all --grep="6346\|search_back\|look_back" 2>/dev/null | head -20

Repository: ChainSafe/forest

Length of output: 547


🏁 Script executed:

# Check the full condition more carefully - is there a bug with the condition?
# limit < current_epoch might miss a case
sed -n '1110,1128p' ./src/state_manager/mod.rs

# Let's trace through some examples:
# Example 1: limit=1000, current_epoch=100
# Condition: limit > 0 && limit < current_epoch = 1000 > 0 && 1000 < 100 = FALSE
# Falls through to _ => 0 (search to genesis) - CORRECT

# Example 2: limit=5, current_epoch=1000
# Condition: limit > 0 && limit < current_epoch = 5 > 0 && 5 < 1000 = TRUE
# min_epoch = 1000 - 5 + 1 = 996
# Searches [996, 997, 998, 999, 1000] = 5 epochs - CORRECT

# Example 3: limit=1000, current_epoch=1000
# Condition: limit > 0 && limit < current_epoch = 1000 > 0 && 1000 < 1000 = FALSE
# Falls through to _ => 0 (search to genesis) - CORRECT per docs

echo "Code appears correct. Verifying there are NO tests for search_back_for_message:"
grep -n "search_back_for_message\|search_for_message" ./src/state_manager/tests.rs

Repository: ChainSafe/forest

Length of output: 1019


🏁 Script executed:

# Check what LOOKBACK_NO_LIMIT is defined as
grep -rn "LOOKBACK_NO_LIMIT" ./src --include="*.rs"

Repository: ChainSafe/forest

Length of output: 390


🏁 Script executed:

# Check the docstring more carefully for the complete parameter documentation
sed -n '1095,1115p' ./src/state_manager/mod.rs

Repository: ChainSafe/forest

Length of output: 1075


🏁 Script executed:

# Final verification: check if check_search is where the actual searching happens
grep -n "fn check_search" ./src/state_manager/mod.rs

# Get the signature and a bit of its implementation
sed -n '/fn check_search/,+30p' ./src/state_manager/mod.rs | head -40

Repository: ChainSafe/forest

Length of output: 1401


🏁 Script executed:

# Verify the formula one more time with a different edge case
# If limit = 10, current_epoch = 100:
# min_epoch = 100 - 10 + 1 = 91
# Epochs searched: [91, 92, ..., 100] = 10 epochs ✓

# Confirm there are no other versions of this function
grep -n "search_back_for_message" ./src/state_manager/mod.rs

Repository: ChainSafe/forest

Length of output: 356


Core fix is correct—add tests before merging.

The implementation correctly converts relative look_back_limit to absolute min_epoch. Code verification confirms:

  • Math is accurate: limit=5 at epoch 1000 → min_epoch=996 → check_search uses while current.epoch() >= min_epoch loop, searching [996-1000] = 5 epochs ✓
  • Edge cases handled properly: Some(0) returns immediately; Some(N) where N≥current_epoch and None both default to 0 (genesis); Some(-1/LOOKBACK_NO_LIMIT) falls through to 0 ✓
  • Condition logic correct: Some(limit) if limit > 0 && limit < current_epoch properly gates the calculation ✓
  • Semantic fix confirmed: Docstring specifies "number of epochs to search backwards" (relative), which code now implements ✓

Critical issue: No tests exist for search_back_for_message or search_for_message. Since this function handles message receipt lookups for RPC methods (StateSearchMsg, StateSearchMsgLimited), add unit tests covering:

  • limit=0 (returns None immediately)
  • limit=N where 0<N<current_epoch (searches exactly N epochs)
  • limit≥current_epoch (searches to genesis)
  • None and Some(-1) (both search to genesis)

Copy link
Member

Choose a reason for hiding this comment

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

@akaladarshi It'd be actually nice to implement some tests here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

pub use jsonrpsee::core::ClientError;

const LOOKBACK_NO_LIMIT: ChainEpoch = -1;
pub const LOOKBACK_NO_LIMIT: ChainEpoch = -1;
Copy link
Member

Choose a reason for hiding this comment

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

Let's document this constant while we're at it; the name is not self-explanatory.

message: &ChainMessage,
look_back_limit: Option<i64>,
allow_replaced: Option<bool>,
min_epoch: ChainEpoch,
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps lookback_max_epoch. min is theoretically correct, but with lookback to me the meaning is a bit inverted. Not a huge deal, if you don't agree let's leave it as is (but add lookback somewhere).

Comment on lines +1101 to +1104
/// - `None` or `Some(-1)` (LOOKBACK_NO_LIMIT): Search all the way to genesis
/// - `Some(0)`: No search performed, returns `None` immediately
/// - `Some(N)` where `N > 0`: Search back at most N epochs
/// - `Some(N)` where `N >= current.epoch()`: Same as unlimited (to genesis)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// - `None` or `Some(-1)` (LOOKBACK_NO_LIMIT): Search all the way to genesis
/// - `Some(0)`: No search performed, returns `None` immediately
/// - `Some(N)` where `N > 0`: Search back at most N epochs
/// - `Some(N)` where `N >= current.epoch()`: Same as unlimited (to genesis)

Let's not AI generate more documentation than there's actually code.

@LesnyRumcajs
Copy link
Member

Also, no green checkmark ❌

@akaladarshi akaladarshi marked this pull request as draft December 17, 2025 05:08
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.

The limit Parameter in API's where look-backs are required is not used correctly

3 participants