From 5c1ca4da09626771a8ad79ce3c80777d9a97e024 Mon Sep 17 00:00:00 2001 From: elmattic Date: Wed, 21 Aug 2024 11:45:27 +0200 Subject: [PATCH 1/5] Add implementation of EthCall method --- src/rpc/methods/eth.rs | 35 +++++++++++++++++++++++++++++++++ src/rpc/mod.rs | 1 + src/tool/subcommands/api_cmd.rs | 17 ++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/src/rpc/methods/eth.rs b/src/rpc/methods/eth.rs index f5b605726a5b..d26d05897b71 100644 --- a/src/rpc/methods/eth.rs +++ b/src/rpc/methods/eth.rs @@ -1870,6 +1870,41 @@ 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 { + // As far as I can tell, the Eth API always returns empty on contract deployment + Ok(EthBytes::default()) + } else { + let msg_rct = invoke_result.msg_rct.context("no message receipt")?; + + let get_bytecode_return: GetBytecodeReturn = + fvm_ipld_encoding::from_slice(msg_rct.return_data().as_slice())?; + if let Some(cid) = get_bytecode_return.0 { + Ok(EthBytes(ctx.store().get_required(&cid)?)) + } else { + Ok(Default::default()) + } + } + } +} + #[cfg(test)] mod test { use super::*; 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..79ad97edf1b2 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 { + from: None, + to: None, + gas: Uint64::default(), + gas_price: EthBigInt::default(), + data: EthBytes::default(), + value: EthBigInt::default(), + }, + BlockNumberOrHash::from_predefined(Predefined::Latest), + ), + use_alias, + ) + .unwrap(), + )); } tests } From f056733daf3a5d99079be799afbbb93515340646 Mon Sep 17 00:00:00 2001 From: elmattic Date: Thu, 22 Aug 2024 10:45:33 +0200 Subject: [PATCH 2/5] Add correct test payload --- src/rpc/methods/eth/types.rs | 12 ++++++++++++ src/tool/subcommands/api_cmd.rs | 12 ++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) 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/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index 79ad97edf1b2..216712d8b249 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -1261,12 +1261,12 @@ fn eth_tests() -> Vec { EthCall::request_with_alias( ( EthCallMessage { - from: None, - to: None, - gas: Uint64::default(), - gas_price: EthBigInt::default(), - data: EthBytes::default(), - value: EthBigInt::default(), + to: Some( + EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb") + .unwrap(), + ), + data: "0xf8b2cb4f000000000000000000000000CbfF24DED1CE6B53712078759233Ac8f91ea71B6".parse().unwrap(), + ..EthCallMessage::default() }, BlockNumberOrHash::from_predefined(Predefined::Latest), ), From 69a88a7a19cc1994e9a9f9b92ee050de62f7a386 Mon Sep 17 00:00:00 2001 From: elmattic Date: Thu, 22 Aug 2024 11:27:47 +0200 Subject: [PATCH 3/5] Fix decoding of the payload --- src/rpc/methods/eth.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/rpc/methods/eth.rs b/src/rpc/methods/eth.rs index d26d05897b71..d50f01eda6b0 100644 --- a/src/rpc/methods/eth.rs +++ b/src/rpc/methods/eth.rs @@ -1894,13 +1894,8 @@ impl RpcMethod<2> for EthCall { } else { let msg_rct = invoke_result.msg_rct.context("no message receipt")?; - let get_bytecode_return: GetBytecodeReturn = - fvm_ipld_encoding::from_slice(msg_rct.return_data().as_slice())?; - if let Some(cid) = get_bytecode_return.0 { - Ok(EthBytes(ctx.store().get_required(&cid)?)) - } else { - Ok(Default::default()) - } + let bytes = decode_payload(&msg_rct.return_data(), CBOR)?; + Ok(bytes) } } } From 5db9e3e2437012fcbe60cb6779e293a456fc2a45 Mon Sep 17 00:00:00 2001 From: elmattic Date: Thu, 22 Aug 2024 11:48:53 +0200 Subject: [PATCH 4/5] Update Changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65d0d6a0b305..b7fc77231310 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 From 6da114e2bcb9cc78d0231b7164ab2c830a62edef Mon Sep 17 00:00:00 2001 From: elmattic Date: Thu, 22 Aug 2024 13:53:05 +0200 Subject: [PATCH 5/5] Remove comment --- src/rpc/methods/eth.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rpc/methods/eth.rs b/src/rpc/methods/eth.rs index d50f01eda6b0..8754b40b16c8 100644 --- a/src/rpc/methods/eth.rs +++ b/src/rpc/methods/eth.rs @@ -1889,7 +1889,6 @@ impl RpcMethod<2> for EthCall { let invoke_result = ctx.state_manager.call(&msg, Some(ts))?; if msg.to() == FilecoinAddress::ETHEREUM_ACCOUNT_MANAGER_ACTOR { - // As far as I can tell, the Eth API always returns empty on contract deployment Ok(EthBytes::default()) } else { let msg_rct = invoke_result.msg_rct.context("no message receipt")?;