diff --git a/CHANGELOG.md b/CHANGELOG.md index cca9e09f49a7..7033f7869f77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ - [#6707](https://github.com/ChainSafe/forest/issues/6707): Added missing `GoldenWeek` network upgrade entry in `Filecoin.StateGetNetworkParams` RPC method. +- [#6817](https://github.com/ChainSafe/forest/pull/6817): Fixed `StateSearchMsg` to return null instead of error when not found to match Lotus behaviour. + ## Forest v0.32.4 "Mild Inconvenience" This is a non-mandatory release for all node operators. It enables F3 finality resolution on ETH v1 RPC methods. diff --git a/mise.toml b/mise.toml index c922f966e953..669d89f21f47 100644 --- a/mise.toml +++ b/mise.toml @@ -241,8 +241,7 @@ tail -n +2 .config/forest.dic | diff - "$TMPFILE" [tasks.insta] description = "Update insta snapshots." run = ''' -cargo test --lib -- rpc::tests::openrpc -cargo insta accept +cargo test --lib -- rpc::tests::openrpc || cargo insta accept ''' tools.cargo-insta = "1.46" diff --git a/src/rpc/methods/state.rs b/src/rpc/methods/state.rs index d786d9df31ad..93dbc80bbe84 100644 --- a/src/rpc/methods/state.rs +++ b/src/rpc/methods/state.rs @@ -1267,11 +1267,12 @@ impl RpcMethod<4> for StateSearchMsg { ["tipsetKey", "messageCid", "lookBackLimit", "allowReplaced"]; const API_PATHS: BitFlags = ApiPaths::all(); const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = - Some("Returns the receipt and tipset the specified message was included in."); + const DESCRIPTION: Option<&'static str> = Some( + "Returns the receipt and tipset the specified message was included in, or null if the message was not found.", + ); type Params = (ApiTipsetKey, Cid, i64, bool); - type Ok = MessageLookup; + type Ok = Option; async fn handle( ctx: Ctx, @@ -1281,7 +1282,7 @@ impl RpcMethod<4> for StateSearchMsg { let from = tsk .map(|k| ctx.chain_index().load_required_tipset(&k)) .transpose()?; - let (tipset, receipt) = ctx + let Some((tipset, receipt)) = ctx .state_manager .search_for_message( from, @@ -1290,15 +1291,17 @@ impl RpcMethod<4> for StateSearchMsg { Some(allow_replaced), ) .await? - .with_context(|| format!("message {message_cid} not found."))?; + else { + return Ok(None); + }; let ipld = receipt.return_data().deserialize().unwrap_or(Ipld::Null); - Ok(MessageLookup { + Ok(Some(MessageLookup { receipt, tipset: tipset.key().clone(), height: tipset.epoch(), message: message_cid, return_dec: ipld, - }) + })) } } @@ -1311,31 +1314,31 @@ impl RpcMethod<2> for StateSearchMsgLimited { 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.", + "Looks back up to limit epochs in the chain for a message, and returns its receipt and the tipset where it was executed, or null if it was not found.", ); type Params = (Cid, i64); - type Ok = MessageLookup; + type Ok = Option; async fn handle( ctx: Ctx, (message_cid, look_back_limit): Self::Params, _: &http::Extensions, ) -> Result { - let (tipset, receipt) = ctx + let Some((tipset, receipt)) = ctx .state_manager .search_for_message(None, message_cid, Some(look_back_limit), None) .await? - .with_context(|| { - format!("message {message_cid} not found within the last {look_back_limit} epochs") - })?; + else { + return Ok(None); + }; let ipld = receipt.return_data().deserialize().unwrap_or(Ipld::Null); - Ok(MessageLookup { + Ok(Some(MessageLookup { receipt, tipset: tipset.key().clone(), height: tipset.epoch(), message: message_cid, return_dec: ipld, - }) + })) } } diff --git a/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap b/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap index 5d385bf493fa..78f1cb5494ef 100644 --- a/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap +++ b/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap @@ -3515,7 +3515,7 @@ methods: $ref: "#/components/schemas/ApiInvocResult" paramStructure: by-position - name: Filecoin.StateSearchMsg - description: Returns the receipt and tipset the specified message was included in. + description: "Returns the receipt and tipset the specified message was included in, or null if the message was not found." params: - name: tipsetKey required: true @@ -3540,12 +3540,14 @@ methods: type: boolean result: name: Filecoin.StateSearchMsg.Result - required: true + required: false schema: - $ref: "#/components/schemas/MessageLookup" + anyOf: + - $ref: "#/components/schemas/MessageLookup" + - type: "null" paramStructure: by-position - name: Filecoin.StateSearchMsgLimited - description: "Looks back up to limit epochs in the chain for a message, and returns its receipt and the tipset where it was executed." + description: "Looks back up to limit epochs in the chain for a message, and returns its receipt and the tipset where it was executed, or null if it was not found." params: - name: message_cid required: true @@ -3558,9 +3560,11 @@ methods: format: int64 result: name: Filecoin.StateSearchMsgLimited.Result - required: true + required: false schema: - $ref: "#/components/schemas/MessageLookup" + anyOf: + - $ref: "#/components/schemas/MessageLookup" + - type: "null" paramStructure: by-position - name: Filecoin.StateSectorExpiration description: Returns the epoch at which the specified sector will expire. diff --git a/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap b/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap index 7a718c0d6c61..6045bf27290f 100644 --- a/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap +++ b/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap @@ -3575,7 +3575,7 @@ methods: $ref: "#/components/schemas/ApiInvocResult" paramStructure: by-position - name: Filecoin.StateSearchMsg - description: Returns the receipt and tipset the specified message was included in. + description: "Returns the receipt and tipset the specified message was included in, or null if the message was not found." params: - name: tipsetKey required: true @@ -3600,9 +3600,11 @@ methods: type: boolean result: name: Filecoin.StateSearchMsg.Result - required: true + required: false schema: - $ref: "#/components/schemas/MessageLookup" + anyOf: + - $ref: "#/components/schemas/MessageLookup" + - type: "null" paramStructure: by-position - name: Filecoin.StateSectorExpiration description: Returns the epoch at which the specified sector will expire. diff --git a/src/tool/subcommands/api_cmd/api_compare_tests.rs b/src/tool/subcommands/api_cmd/api_compare_tests.rs index 8490123bff31..3339b32f622e 100644 --- a/src/tool/subcommands/api_cmd/api_compare_tests.rs +++ b/src/tool/subcommands/api_cmd/api_compare_tests.rs @@ -1226,11 +1226,11 @@ fn state_tests_with_tipset( for msg_cid in sample_message_cids(bls_messages.iter(), secp_messages.iter()) { tests.extend([ RpcTest::identity(StateReplay::request((tipset.key().into(), msg_cid))?), - validate_message_lookup( + validate_message_wait( StateWaitMsg::request((msg_cid, 0, 10101, true))? .with_timeout(Duration::from_secs(15)), ), - validate_message_lookup( + validate_message_wait( StateWaitMsg::request((msg_cid, 0, 10101, false))? .with_timeout(Duration::from_secs(15)), ), @@ -2780,7 +2780,7 @@ fn dump_test_data(dump_dir: &Path, success: bool, test_dump: &TestDump) -> anyho Ok(()) } -fn validate_message_lookup(req: rpc::Request) -> RpcTest { +fn validate_message_wait(req: rpc::Request) -> RpcTest { RpcTest::validate(req, |mut forest, mut lotus| { // TODO(hanabi1224): https://github.com/ChainSafe/forest/issues/3784 forest.return_dec = Ipld::Null; @@ -2789,6 +2789,19 @@ fn validate_message_lookup(req: rpc::Request) -> RpcTest { }) } +fn validate_message_lookup(req: rpc::Request>) -> RpcTest { + RpcTest::validate(req, |mut forest, mut lotus| { + // TODO(hanabi1224): https://github.com/ChainSafe/forest/issues/3784 + if let Some(forest) = &mut forest { + forest.return_dec = Ipld::Null; + } + if let Some(lotus) = &mut lotus { + lotus.return_dec = Ipld::Null; + } + forest == lotus + }) +} + fn validate_tagged_tipset_v2(req: rpc::Request, offline: bool) -> RpcTest { RpcTest::validate(req, move |forest, lotus| { if offline {