From b25bab93947fa65986abc79ea4c31464d794a18d Mon Sep 17 00:00:00 2001 From: Aryan Tikarya Date: Tue, 16 Dec 2025 13:12:38 +0530 Subject: [PATCH 1/2] fix: convert look back limit to actual epoch before searching for msg --- src/rpc/methods/state.rs | 5 +++-- src/rpc/mod.rs | 2 +- src/state_manager/mod.rs | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/rpc/methods/state.rs b/src/rpc/methods/state.rs index 3689c9681b78..aad06aed83e6 100644 --- a/src/rpc/methods/state.rs +++ b/src/rpc/methods/state.rs @@ -1266,7 +1266,6 @@ impl RpcMethod<4> for StateSearchMsg { } } -/// Looks back up to limit epochs in the chain for a message, and returns its receipt and the tipset where it was executed. /// See pub enum StateSearchMsgLimited {} @@ -1275,7 +1274,9 @@ impl RpcMethod<2> for StateSearchMsgLimited { const PARAM_NAMES: [&'static str; 2] = ["message_cid", "look_back_limit"]; const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V0); // Not supported in V1 const PERMISSION: Permission = Permission::Read; - + const DESCRIPTION: Option<&'static str> = Some( + "Looks back up to limit epochs in the chain for a message, and returns its receipt and the tipset where it was executed.", + ); type Params = (Cid, i64); type Ok = MessageLookup; diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index dcc5bfdb8fa9..221e57d13d1b 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -41,7 +41,7 @@ pub use methods::*; /// Protocol or transport-specific error pub use jsonrpsee::core::ClientError; -const LOOKBACK_NO_LIMIT: ChainEpoch = -1; +pub const LOOKBACK_NO_LIMIT: ChainEpoch = -1; /// The macro `callback` will be passed in each type that implements /// [`RpcMethod`]. diff --git a/src/state_manager/mod.rs b/src/state_manager/mod.rs index 0cb6230f7c30..d302f68d848c 100644 --- a/src/state_manager/mod.rs +++ b/src/state_manager/mod.rs @@ -1046,17 +1046,17 @@ where &self, mut current: Tipset, message: &ChainMessage, - look_back_limit: Option, - allow_replaced: Option, + min_epoch: ChainEpoch, + allow_replaced: bool, ) -> Result, Error> { - let allow_replaced = allow_replaced.unwrap_or(true); let message_from_address = message.from(); let message_sequence = message.sequence(); let mut current_actor_state = self .get_required_actor(&message_from_address, *current.parent_state()) .map_err(Error::state)?; let message_from_id = self.lookup_required_id(&message_from_address, ¤t)?; - while current.epoch() > look_back_limit.unwrap_or_default() { + + while current.epoch() >= min_epoch { let parent_tipset = self .chain_index() .load_required_tipset(current.parents()) @@ -1091,6 +1091,17 @@ where Ok(None) } + /// Searches backwards through the chain for a message receipt. + /// + /// # Arguments + /// * `current` - The tipset to start searching from + /// * `message` - The message to search for + /// * `look_back_limit` - Maximum number of epochs to search backwards: + /// - `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) + /// * `allow_replaced` - Whether to accept replacement messages (same nonce, different CID) fn search_back_for_message( &self, current: Tipset, @@ -1098,7 +1109,21 @@ where look_back_limit: Option, allow_replaced: Option, ) -> Result, 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) } /// Returns a message receipt from a given tipset and message CID. From 0e194768bbb61fc92693451ae6374c3a5cf75d7b Mon Sep 17 00:00:00 2001 From: Aryan Tikarya Date: Fri, 9 Jan 2026 15:30:54 +0530 Subject: [PATCH 2/2] fix failing tests and address comments --- src/rpc/mod.rs | 1 + src/state_manager/mod.rs | 20 +++++-------------- .../subcommands/api_cmd/test_snapshots.txt | 4 ++-- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 221e57d13d1b..1903d4d05fe2 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -41,6 +41,7 @@ pub use methods::*; /// Protocol or transport-specific error pub use jsonrpsee::core::ClientError; +/// Sentinel value, indicating no limit on how far back to search in the chain (all the way to genesis epoch). pub const LOOKBACK_NO_LIMIT: ChainEpoch = -1; /// The macro `callback` will be passed in each type that implements diff --git a/src/state_manager/mod.rs b/src/state_manager/mod.rs index d302f68d848c..b41c0ce064d5 100644 --- a/src/state_manager/mod.rs +++ b/src/state_manager/mod.rs @@ -1046,7 +1046,7 @@ where &self, mut current: Tipset, message: &ChainMessage, - min_epoch: ChainEpoch, + lookback_max_epoch: ChainEpoch, allow_replaced: bool, ) -> Result, Error> { let message_from_address = message.from(); @@ -1056,7 +1056,7 @@ where .map_err(Error::state)?; let message_from_id = self.lookup_required_id(&message_from_address, ¤t)?; - while current.epoch() >= min_epoch { + while current.epoch() >= lookback_max_epoch { let parent_tipset = self .chain_index() .load_required_tipset(current.parents()) @@ -1092,16 +1092,6 @@ where } /// Searches backwards through the chain for a message receipt. - /// - /// # Arguments - /// * `current` - The tipset to start searching from - /// * `message` - The message to search for - /// * `look_back_limit` - Maximum number of epochs to search backwards: - /// - `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) - /// * `allow_replaced` - Whether to accept replacement messages (same nonce, different CID) fn search_back_for_message( &self, current: Tipset, @@ -1112,8 +1102,8 @@ where 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 { + // Calculate the max lookback epoch (inclusive lower bound) for the search. + let lookback_max_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 @@ -1123,7 +1113,7 @@ where _ => 0, }; - self.check_search(current, message, min_epoch, allow_replaced) + self.check_search(current, message, lookback_max_epoch, allow_replaced) } /// Returns a message receipt from a given tipset and message CID. diff --git a/src/tool/subcommands/api_cmd/test_snapshots.txt b/src/tool/subcommands/api_cmd/test_snapshots.txt index 2e645609c48f..79e8e4a5e09d 100644 --- a/src/tool/subcommands/api_cmd/test_snapshots.txt +++ b/src/tool/subcommands/api_cmd/test_snapshots.txt @@ -286,8 +286,8 @@ filecoin_statereadstate_1748444218790447.rpcsnap.json.zst filecoin_statereadstate_1748444218790807.rpcsnap.json.zst filecoin_statereadstate_1749657617007020.rpcsnap.json.zst filecoin_statereplay_1743504051038215.rpcsnap.json.zst -filecoin_statesearchmsg_1741784596636715.rpcsnap.json.zst -filecoin_statesearchmsglimited_1741784596704877.rpcsnap.json.zst +filecoin_statesearchmsg_1767947430707712.rpcsnap.json.zst +filecoin_statesearchmsglimited_1767936905056090.rpcsnap.json.zst filecoin_statesectorexpiration_1741784727996672.rpcsnap.json.zst filecoin_statesectorgetinfo_1743098602783105.rpcsnap.json.zst filecoin_statesectorpartition_1737546933391247.rpcsnap.json.zst