diff --git a/src/lotus_json/actor_states/methods/evm_actor_params.rs b/src/lotus_json/actor_states/methods/evm_actor_params.rs new file mode 100644 index 000000000000..8cde8409f13e --- /dev/null +++ b/src/lotus_json/actor_states/methods/evm_actor_params.rs @@ -0,0 +1,158 @@ +// Copyright 2019-2025 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT +use super::*; +use crate::rpc::eth::types::GetStorageAtParams; +use crate::shim::econ::TokenAmount; +use ::cid::Cid; +use fvm_ipld_encoding::RawBytes; +use paste::paste; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; + +#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)] +#[serde(rename_all = "PascalCase")] +pub struct EVMConstructorParamsLotusJson { + pub creator: [u8; 20], + #[schemars(with = "LotusJson")] + #[serde(with = "crate::lotus_json")] + pub initcode: RawBytes, +} + +macro_rules! impl_evm_constructor_params { + ($($version:literal),+) => { + $( + paste! { + impl HasLotusJson for fil_actor_evm_state::[]::ConstructorParams { + type LotusJson = EVMConstructorParamsLotusJson; + + #[cfg(test)] + fn snapshots() -> Vec<(serde_json::Value, Self)> { + vec![ + ( + json!({ + "Creator": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], + "Initcode": "ESIzRFU=" + }), + Self { + creator: fil_actor_evm_state::evm_shared::[]::address::EthAddress([0; 20]), + initcode: RawBytes::new(hex::decode("1122334455").unwrap()), + }, + ), + ] + } + + fn into_lotus_json(self) -> Self::LotusJson { + EVMConstructorParamsLotusJson { + creator: self.creator.0, + initcode: self.initcode, + } + } + + fn from_lotus_json(lotus_json: Self::LotusJson) -> Self { + Self { + creator: fil_actor_evm_state::evm_shared::[]::address::EthAddress(lotus_json.creator), + initcode: lotus_json.initcode, + } + } + } + } + )+ + }; +} + +impl_evm_constructor_params!(10, 11, 12, 13, 14, 15, 16); + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "PascalCase")] +pub struct EVMDelegateCallParamsLotusJson { + #[schemars(with = "LotusJson")] + #[serde(with = "crate::lotus_json")] + pub code: Cid, + #[schemars(with = "LotusJson")] + #[serde(with = "crate::lotus_json")] + pub input: RawBytes, + pub caller: [u8; 20], + #[schemars(with = "LotusJson")] + #[serde(with = "crate::lotus_json")] + pub value: TokenAmount, +} + +macro_rules! impl_evm_delegate_call_params_lotus_json { +($($version:literal),+) => { + $( + paste! { + impl HasLotusJson for fil_actor_evm_state::[]::DelegateCallParams { + type LotusJson = EVMDelegateCallParamsLotusJson; + + #[cfg(test)] + fn snapshots() -> Vec<(serde_json::Value, Self)> { + vec![( + json!({ + "Code": "bafy2bzaceaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "Input": "ESIzRFU=", + "Caller": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], + "Value": "0" + }), + Self { + code: Cid::default(), + input: hex::decode("1122334455").unwrap(), + caller: fil_actor_evm_state::evm_shared::[]::address::EthAddress([0; 20]), + value: TokenAmount::from_atto(0).into(), + }, + )] + } + + fn into_lotus_json(self) -> Self::LotusJson { + EVMDelegateCallParamsLotusJson { + code: self.code, + input: self.input.into(), + caller: self.caller.0, + value: self.value.into(), + } + } + fn from_lotus_json(lotus_json: Self::LotusJson) -> Self { + Self { + code: lotus_json.code, + input: lotus_json.input.into(), + caller: fil_actor_evm_state::evm_shared::[]::address::EthAddress(lotus_json.caller), + value: lotus_json.value.into(), + } + } + }} + )+ + }; +} + +impl_evm_delegate_call_params_lotus_json!(10, 11, 12, 13, 14, 15, 16); + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "PascalCase")] +pub struct GetStorageAtParamsLotusJson { + pub storage_key: [u8; 32], +} + +impl HasLotusJson for GetStorageAtParams { + type LotusJson = GetStorageAtParamsLotusJson; + + #[cfg(test)] + fn snapshots() -> Vec<(serde_json::Value, Self)> { + vec![( + json!({ + "StorageKey": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10] + }), + GetStorageAtParams::new(vec![0xa]).unwrap(), + )] + } + + fn into_lotus_json(self) -> Self::LotusJson { + GetStorageAtParamsLotusJson { + storage_key: self.0, + } + } + + fn from_lotus_json(lotus_json: Self::LotusJson) -> Self { + GetStorageAtParams::new(lotus_json.storage_key.to_vec()) + .expect("expected array to have 32 elements") + } +} diff --git a/src/lotus_json/actor_states/methods/evm_constructor_params.rs b/src/lotus_json/actor_states/methods/evm_constructor_params.rs deleted file mode 100644 index a9543dae17f9..000000000000 --- a/src/lotus_json/actor_states/methods/evm_constructor_params.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019-2025 ChainSafe Systems -// SPDX-License-Identifier: Apache-2.0, MIT -use super::*; -use fvm_ipld_encoding::RawBytes; -use jsonrpsee::core::Serialize; -use paste::paste; -use schemars::JsonSchema; -use serde::Deserialize; -use std::fmt::Debug; - -#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)] -#[serde(rename_all = "PascalCase")] -pub struct EVMConstructorParamsLotusJson { - pub creator: [u8; 20], - #[schemars(with = "LotusJson")] - #[serde(with = "crate::lotus_json")] - pub initcode: RawBytes, -} - -macro_rules! impl_evm_constructor_params { - ($($version:literal),+) => { - $( - paste! { - impl HasLotusJson for fil_actor_evm_state::[]::ConstructorParams { - type LotusJson = EVMConstructorParamsLotusJson; - - #[cfg(test)] - fn snapshots() -> Vec<(serde_json::Value, Self)> { - vec![ - ( - json!({ - "Creator": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], - "Initcode": "ESIzRFU=" - }), - Self { - creator: fil_actor_evm_state::evm_shared::[]::address::EthAddress([0; 20]), - initcode: RawBytes::new(hex::decode("1122334455").unwrap()), - }, - ), - ] - } - - fn into_lotus_json(self) -> Self::LotusJson { - EVMConstructorParamsLotusJson { - creator: self.creator.0, - initcode: self.initcode, - } - } - - fn from_lotus_json(lotus_json: Self::LotusJson) -> Self { - Self { - creator: fil_actor_evm_state::evm_shared::[]::address::EthAddress(lotus_json.creator), - initcode: lotus_json.initcode, - } - } - } - } - )+ - }; -} - -impl_evm_constructor_params!(10, 11, 12, 13, 14, 15, 16); diff --git a/src/lotus_json/actor_states/methods/mod.rs b/src/lotus_json/actor_states/methods/mod.rs index 26306884b914..02c6312aa7e3 100644 --- a/src/lotus_json/actor_states/methods/mod.rs +++ b/src/lotus_json/actor_states/methods/mod.rs @@ -6,7 +6,7 @@ mod account_authenticate_params; mod account_constructor_params; mod cron_actor_params; mod datacap_actor_params; -mod evm_constructor_params; +mod evm_actor_params; mod init_constructor_params; mod init_exec4_params; mod init_exec_params; diff --git a/src/rpc/methods/eth/types.rs b/src/rpc/methods/eth/types.rs index 98c442715b2f..a9e4e24431e7 100644 --- a/src/rpc/methods/eth/types.rs +++ b/src/rpc/methods/eth/types.rs @@ -62,8 +62,9 @@ impl FromStr for EthBytes { pub struct GetBytecodeReturn(pub Option); const GET_STORAGE_AT_PARAMS_ARRAY_LENGTH: usize = 32; +const LENGTH_BUF_GET_STORAGE_AT_PARAMS: u8 = 129; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct GetStorageAtParams(pub [u8; GET_STORAGE_AT_PARAMS_ARRAY_LENGTH]); impl GetStorageAtParams { @@ -80,11 +81,20 @@ impl GetStorageAtParams { } pub fn serialize_params(&self) -> anyhow::Result> { - const LENGTH_BUF_GET_STORAGE_AT_PARAMS: u8 = 129; - let mut encoded = fvm_ipld_encoding::to_vec(&RawBytes::new(self.0.to_vec()))?; - encoded.insert(0, LENGTH_BUF_GET_STORAGE_AT_PARAMS); + let mut encoded = vec![LENGTH_BUF_GET_STORAGE_AT_PARAMS]; + fvm_ipld_encoding::to_writer(&mut encoded, &RawBytes::new(self.0.to_vec()))?; Ok(encoded) } + + pub fn deserialize_params(bz: &[u8]) -> anyhow::Result { + let (&prefix, bytes) = bz.split_first().context("unexpected EOF")?; + ensure!( + prefix == LENGTH_BUF_GET_STORAGE_AT_PARAMS, + "expected CBOR array of length 1" + ); + let decoded: RawBytes = fvm_ipld_encoding::from_slice(bytes)?; + GetStorageAtParams::new(decoded.into()) + } } #[derive( diff --git a/src/rpc/registry/actors/evm.rs b/src/rpc/registry/actors/evm.rs index 99114b21a339..d2536d18fc38 100644 --- a/src/rpc/registry/actors/evm.rs +++ b/src/rpc/registry/actors/evm.rs @@ -1,18 +1,40 @@ // Copyright 2019-2025 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use crate::rpc::eth::types::GetStorageAtParams; use crate::rpc::registry::methods_reg::{MethodRegistry, register_actor_methods}; use crate::shim::message::MethodNum; use cid::Cid; +use fvm_ipld_encoding::RawBytes; macro_rules! register_evm_version { ($registry:expr, $code_cid:expr, $state_version:path) => {{ - use $state_version::{ConstructorParams, Method}; + use $state_version::{ConstructorParams, DelegateCallParams, Method}; register_actor_methods!( $registry, $code_cid, - [(Method::Constructor, ConstructorParams)] + [ + (Method::Constructor, ConstructorParams), + (Method::Resurrect, ConstructorParams), + (Method::InvokeContract, RawBytes), + (Method::InvokeContractDelegate, DelegateCallParams), + ] + ); + + $registry.register_method( + $code_cid, + Method::GetStorageAt as MethodNum, + GetStorageAtParams::deserialize_params, + ); + + register_actor_methods!( + $registry, + $code_cid, + [ + (Method::GetBytecode, empty), + (Method::GetBytecodeHash, empty) + ] ); }}; } diff --git a/src/tool/subcommands/api_cmd/api_compare_tests.rs b/src/tool/subcommands/api_cmd/api_compare_tests.rs index f2c91db95558..1b28f2e57e0e 100644 --- a/src/tool/subcommands/api_cmd/api_compare_tests.rs +++ b/src/tool/subcommands/api_cmd/api_compare_tests.rs @@ -1836,10 +1836,6 @@ fn eth_tests_with_tipset(store: &Arc, shared_tipset: &Tipset } fn state_decode_params_api_tests(tipset: &Tipset) -> anyhow::Result> { - let evm_constructor_params = fil_actor_evm_state::v16::ConstructorParams { - creator: fil_actor_evm_state::evm_shared::v16::address::EthAddress([0; 20]), - initcode: fvm_ipld_encoding::RawBytes::new(vec![0x12, 0x34, 0x56]), // dummy bytecode - }; // // TODO(go-state-types): https://github.com/filecoin-project/go-state-types/issues/396 // // Enable this test when lotus supports it in go-state-types. // let cron_constructor_params = fil_actor_cron_state::v16::ConstructorParams { @@ -1849,12 +1845,6 @@ fn state_decode_params_api_tests(tipset: &Tipset) -> anyhow::Result // }], // }; let mut tests = vec![ - RpcTest::identity(StateDecodeParams::request(( - Address::from_str(EVM_ADDRESS).unwrap(), // evm actor - 1, - to_vec(&evm_constructor_params)?, - tipset.key().into(), - ))?), RpcTest::identity(StateDecodeParams::request(( Address::SYSTEM_ACTOR, fil_actor_system_state::v16::Method::Constructor as u64, @@ -1880,6 +1870,7 @@ fn state_decode_params_api_tests(tipset: &Tipset) -> anyhow::Result tests.extend(miner_actor_state_decode_params_tests(tipset)?); tests.extend(account_actor_state_decode_params_tests(tipset)?); tests.extend(init_actor_state_decode_params_tests(tipset)?); + tests.extend(evm_actor_state_decode_params_tests(tipset)?); tests.extend(reward_actor_state_decode_params_tests(tipset)?); tests.extend(power_actor_state_decode_params_tests(tipset)?); tests.extend(datacap_actor_state_decode_params_tests(tipset)?); @@ -1889,6 +1880,73 @@ fn state_decode_params_api_tests(tipset: &Tipset) -> anyhow::Result Ok(tests) } +fn evm_actor_state_decode_params_tests(tipset: &Tipset) -> anyhow::Result> { + let evm_constructor_params = fil_actor_evm_state::v16::ConstructorParams { + creator: fil_actor_evm_state::evm_shared::v16::address::EthAddress([0; 20]), + initcode: fvm_ipld_encoding::RawBytes::new(vec![0x12, 0x34, 0x56]), // dummy bytecode + }; + + let evm_invoke_contract_params = fil_actor_evm_state::v16::InvokeContractParams { + input_data: vec![0x11, 0x22, 0x33, 0x44, 0x55], // dummy input data + }; + + let evm_delegate_call_params = fil_actor_evm_state::v16::DelegateCallParams { + code: Cid::default(), + input: vec![0x11, 0x22, 0x33, 0x44, 0x55], // dummy input data + caller: fil_actor_evm_state::evm_shared::v16::address::EthAddress([0; 20]), + value: TokenAmount::default().into(), + }; + + let evm_get_storage_at_params = GetStorageAtParams::new(vec![0xa])?; + + let tests = vec![ + RpcTest::identity(StateDecodeParams::request(( + Address::from_str(EVM_ADDRESS).unwrap(), + fil_actor_evm_state::v16::Method::Constructor as u64, + to_vec(&evm_constructor_params)?, + tipset.key().into(), + ))?), + RpcTest::identity(StateDecodeParams::request(( + Address::from_str(EVM_ADDRESS).unwrap(), + fil_actor_evm_state::v16::Method::Resurrect as u64, + to_vec(&evm_constructor_params)?, + tipset.key().into(), + ))?), + RpcTest::identity(StateDecodeParams::request(( + Address::from_str(EVM_ADDRESS).unwrap(), + fil_actor_evm_state::v16::Method::GetBytecode as u64, + vec![], + tipset.key().into(), + ))?), + RpcTest::identity(StateDecodeParams::request(( + Address::from_str(EVM_ADDRESS).unwrap(), + fil_actor_evm_state::v16::Method::GetBytecodeHash as u64, + vec![], + tipset.key().into(), + ))?), + RpcTest::identity(StateDecodeParams::request(( + Address::from_str(EVM_ADDRESS).unwrap(), + fil_actor_evm_state::v16::Method::InvokeContract as u64, + to_vec(&evm_invoke_contract_params)?, + tipset.key().into(), + ))?), + RpcTest::identity(StateDecodeParams::request(( + Address::from_str(EVM_ADDRESS).unwrap(), + fil_actor_evm_state::v16::Method::InvokeContractDelegate as u64, + to_vec(&evm_delegate_call_params)?, + tipset.key().into(), + ))?), + RpcTest::identity(StateDecodeParams::request(( + Address::from_str(EVM_ADDRESS).unwrap(), + fil_actor_evm_state::v16::Method::GetStorageAt as u64, + evm_get_storage_at_params.serialize_params()?, + tipset.key().into(), + ))?), + ]; + + Ok(tests) +} + fn miner_actor_state_decode_params_tests(tipset: &Tipset) -> anyhow::Result> { let miner_constructor_params = fil_actor_miner_state::v16::MinerConstructorParams { owner: Address::new_id(1000).into(), diff --git a/src/tool/subcommands/api_cmd/test_snapshots.txt b/src/tool/subcommands/api_cmd/test_snapshots.txt index 8007de1d90ef..e3eec68b33f0 100644 --- a/src/tool/subcommands/api_cmd/test_snapshots.txt +++ b/src/tool/subcommands/api_cmd/test_snapshots.txt @@ -178,6 +178,13 @@ filecoin_multisig_statedecodeparams_1754230255631872.rpcsnap.json.zst filecoin_multisig_statedecodeparams_1754230255631946.rpcsnap.json.zst filecoin_multisig_statedecodeparams_1754230255632019.rpcsnap.json.zst filecoin_multisig_statedecodeparams_1754581573704814.rpcsnap.json.zst +filecoin_evm_constructor_statedecodeparams_1755004924714690.rpcsnap.json.zst +filecoin_evm_getbytecode_statedecodeparams_1755004924714460.rpcsnap.json.zst +filecoin_evm_getbytecodehash_statedecodeparams_1755004924714337.rpcsnap.json.zst +filecoin_evm_invokecontract_statedecodeparams_1755004924714521.rpcsnap.json.zst +filecoin_evm_invokecontractdelegate_statedecodeparams_1755004924714578.rpcsnap.json.zst +filecoin_evm_resurrect_statedecodeparams_1755004924714636.rpcsnap.json.zst +filecoin_evm_getstorageat_statedecodeparams_1755619756266970.rpcsnap.json.zst filecoin_verified_reg_statedecodeparams_1754401651142334.rpcsnap.json.zst filecoin_verified_reg_statedecodeparams_1754492006350827.rpcsnap.json.zst filecoin_verified_reg_statedecodeparams_1754401651146246.rpcsnap.json.zst