diff --git a/CHANGELOG.md b/CHANGELOG.md index e184d6efd3a9..caa0953bc960 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,9 @@ - [#4463](https://github.com/ChainSafe/forest/issues/4463) Add support for the `Filecoin.EthGetTransactionByHash` RPC method. +- [#4613](https://github.com/ChainSafe/forest/issues/4613) Add support for the + `Filecoin.EthCall` RPC method. + ### Changed - [#4583](https://github.com/ChainSafe/forest/pull/4583) Removed the expiration diff --git a/src/rpc/methods/eth.rs b/src/rpc/methods/eth.rs index f5b605726a5b..8754b40b16c8 100644 --- a/src/rpc/methods/eth.rs +++ b/src/rpc/methods/eth.rs @@ -1870,6 +1870,35 @@ impl RpcMethod<1> for EthGetTransactionHashByCid { } } +pub enum EthCall {} +impl RpcMethod<2> for EthCall { + const NAME: &'static str = "Filecoin.EthCall"; + const NAME_ALIAS: Option<&'static str> = Some("eth_call"); + const N_REQUIRED_PARAMS: usize = 1; + const PARAM_NAMES: [&'static str; 2] = ["tx", "block_param"]; + const API_PATHS: ApiPaths = ApiPaths::V1; + const PERMISSION: Permission = Permission::Read; + type Params = (EthCallMessage, BlockNumberOrHash); + type Ok = EthBytes; + async fn handle( + ctx: Ctx, + (tx, block_param): Self::Params, + ) -> Result { + let msg = tx.try_into()?; + let ts = tipset_by_block_number_or_hash(ctx.chain_store(), block_param)?; + let invoke_result = ctx.state_manager.call(&msg, Some(ts))?; + + if msg.to() == FilecoinAddress::ETHEREUM_ACCOUNT_MANAGER_ACTOR { + Ok(EthBytes::default()) + } else { + let msg_rct = invoke_result.msg_rct.context("no message receipt")?; + + let bytes = decode_payload(&msg_rct.return_data(), CBOR)?; + Ok(bytes) + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/rpc/methods/eth/types.rs b/src/rpc/methods/eth/types.rs index 6c287c9cfa5f..d97a2f06c102 100644 --- a/src/rpc/methods/eth/types.rs +++ b/src/rpc/methods/eth/types.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0, MIT use super::*; +use libipld::error::SerdeError; +use serde::de::{value::StringDeserializer, IntoDeserializer}; pub const METHOD_GET_BYTE_CODE: u64 = 3; pub const METHOD_GET_STORAGE_AT: u64 = 5; @@ -30,6 +32,16 @@ impl From for EthBytes { } } +impl FromStr for EthBytes { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let deserializer: StringDeserializer = String::from_str(s)?.into_deserializer(); + let bytes = crate::lotus_json::hexify_vec_bytes::deserialize(deserializer)?; + Ok(Self(bytes)) + } +} + #[derive(Debug, Deserialize, Serialize)] pub struct GetBytecodeReturn(pub Option); diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 2766d06a794a..90cb2fadd90b 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -87,6 +87,7 @@ macro_rules! for_each_method { $callback!(crate::rpc::eth::EthProtocolVersion); $callback!(crate::rpc::eth::EthGetTransactionByHash); $callback!(crate::rpc::eth::EthGetTransactionHashByCid); + $callback!(crate::rpc::eth::EthCall); // gas vertical $callback!(crate::rpc::gas::GasEstimateGasLimit); diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index 04cbd5e0d59c..216712d8b249 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -1257,6 +1257,23 @@ fn eth_tests() -> Vec { tests.push(RpcTest::identity( EthProtocolVersion::request_with_alias((), use_alias).unwrap(), )); + tests.push(RpcTest::identity( + EthCall::request_with_alias( + ( + EthCallMessage { + to: Some( + EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb") + .unwrap(), + ), + data: "0xf8b2cb4f000000000000000000000000CbfF24DED1CE6B53712078759233Ac8f91ea71B6".parse().unwrap(), + ..EthCallMessage::default() + }, + BlockNumberOrHash::from_predefined(Predefined::Latest), + ), + use_alias, + ) + .unwrap(), + )); } tests }