Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions src/lotus_json/actor_states/methods/eam_params.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright 2019-2025 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use super::*;
use fvm_ipld_encoding::RawBytes;
use paste::paste;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct EAMCreateParamsLotusJson {
#[schemars(with = "LotusJson<RawBytes>")]
#[serde(with = "crate::lotus_json")]
pub initcode: RawBytes,
pub nonce: u64,
}

macro_rules! impl_eam_create_params {
($($version:literal),+) => {
$(
paste! {
impl HasLotusJson for fil_actor_eam_state::[<v $version>]::CreateParams {
type LotusJson = EAMCreateParamsLotusJson;

#[cfg(test)]
fn snapshots() -> Vec<(serde_json::Value, Self)> {
vec![
(
json!({
"Initcode": "ESIzRFU=",
"Nonce": 42
}),
Self {
initcode: hex::decode("1122334455").unwrap(),
nonce: 42,
},
),
]
}

fn into_lotus_json(self) -> Self::LotusJson {
EAMCreateParamsLotusJson {
initcode: RawBytes::new(self.initcode),
nonce: self.nonce,
}
}

fn from_lotus_json(lotus_json: Self::LotusJson) -> Self {
Self {
initcode: lotus_json.initcode.into(),
nonce: lotus_json.nonce,
}
}
}
}
)+
};
}

#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct EAMCreate2ParamsLotusJson {
#[schemars(with = "LotusJson<Vec<u8>>")]
#[serde(with = "crate::lotus_json")]
pub initcode: Vec<u8>,
pub salt: [u8; 32],
}
Comment on lines +63 to +68
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Aug 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix Create2 LotusJson: base64 encoding for Salt, snapshot mismatch, and redundant/incorrect try_into.

  • Salt is presented as a base64 string in the snapshots, but the struct field is [u8; 32] without serde(with = "crate::lotus_json"), so deserialization would expect an array of numbers, not a string.
  • The snapshot’s expected Self uses salt: [0; 32] while the provided base64 decodes to 0..31, causing the snapshot to fail.
  • try_into() on [u8; 32] to [u8; 32] is unnecessary and, as written, can be incorrect depending on trait impl expectations.

Recommend representing Salt in the LotusJson type as Vec<u8> (base64 via crate::lotus_json) and convert to [u8; 32] in from_lotus_json. Also fix the snapshot to reflect the actual bytes (0..31).

 #[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
 #[serde(rename_all = "PascalCase")]
 pub struct EAMCreate2ParamsLotusJson {
     #[schemars(with = "LotusJson<Vec<u8>>")]
     #[serde(with = "crate::lotus_json")]
     pub initcode: Vec<u8>,
-    pub salt: [u8; 32],
+    #[schemars(with = "LotusJson<Vec<u8>>")]
+    #[serde(with = "crate::lotus_json")]
+    pub salt: Vec<u8>,
 }
@@
                     fn snapshots() -> Vec<(serde_json::Value, Self)> {
                         vec![
                             (
                                 json!({
                                     "Initcode": "ESIzRFU=",
                                     "Salt": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="
                                 }),
                                 Self {
                                     initcode: hex::decode("1122334455").unwrap(),
-                                    salt: [0; 32],
+                                    salt: [
+                                        0, 1, 2, 3, 4, 5, 6, 7,
+                                        8, 9, 10, 11, 12, 13, 14, 15,
+                                        16, 17, 18, 19, 20, 21, 22, 23,
+                                        24, 25, 26, 27, 28, 29, 30, 31
+                                    ],
                                 },
                             ),
                         ]
                     }
@@
                     fn into_lotus_json(self) -> Self::LotusJson {
                         EAMCreate2ParamsLotusJson {
                             initcode: self.initcode,
-                            salt: self.salt,
+                            salt: self.salt.to_vec(),
                         }
                     }
 
                     fn from_lotus_json(lotus_json: Self::LotusJson) -> Self {
                         Self {
-                            initcode: lotus_json.initcode.into(),
-                            salt: lotus_json.salt.try_into().unwrap_or_else(|_| [0; 32]),
+                            initcode: lotus_json.initcode,
+                            salt: <[u8; 32]>::try_from(lotus_json.salt).unwrap_or_else(|v: Vec<u8>| {
+                                debug_assert_eq!(v.len(), 32, "EAM Create2 salt must be 32 bytes");
+                                [0; 32]
+                            }),
                         }
                     }

Also applies to: 77-91, 93-105

🤖 Prompt for AI Agents
In src/lotus_json/actor_states/methods/eam_params.rs around lines 63-68 (and
similarly at 77-91, 93-105): the salt field is currently [u8;32] but snapshots
provide base64, and there are redundant/incorrect try_into usages; change the
struct to represent salt as a LotusJson base64-backed Vec<u8> (add
#[schemars(with = "LotusJson<Vec<u8>>")] and #[serde(with =
"crate::lotus_json")] on salt), then in from_lotus_json convert the Vec<u8> into
a [u8;32] by verifying length == 32 and copying into an array (return an error
if length mismatches) instead of using unnecessary try_into on an array; update
the snapshot expected salt bytes to the actual decoded sequence (0..31) so tests
align.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akaladarshi What do we do with this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored the snapshot test


macro_rules! impl_eam_create2_params {
($($version:literal),+) => {
$(
paste! {
impl HasLotusJson for fil_actor_eam_state::[<v $version>]::Create2Params {
type LotusJson = EAMCreate2ParamsLotusJson;

#[cfg(test)]
fn snapshots() -> Vec<(serde_json::Value, Self)> {
vec![
(
json!({
"Initcode": "ESIzRFU=",
"Salt": [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,0]
}),
Self {
initcode: hex::decode("1122334455").unwrap(),
salt: [0; 32],
},
),
]
}

fn into_lotus_json(self) -> Self::LotusJson {
EAMCreate2ParamsLotusJson {
initcode: self.initcode,
salt: self.salt,
}
}

fn from_lotus_json(lotus_json: Self::LotusJson) -> Self {
Self {
initcode: lotus_json.initcode,
salt: lotus_json.salt,
}
}
}
}
)+
};
}

#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct EAMCreateExternalParamsLotusJson(
#[schemars(with = "LotusJson<RawBytes>")]
#[serde(with = "crate::lotus_json")]
RawBytes,
);

macro_rules! impl_eam_create_external_params {
($($version:literal),+) => {
$(
paste! {
impl HasLotusJson for fil_actor_eam_state::[<v $version>]::CreateExternalParams {
type LotusJson = EAMCreateExternalParamsLotusJson;

#[cfg(test)]
fn snapshots() -> Vec<(serde_json::Value, Self)> {
vec![
(
json!("ESIzRFU="),
Self(hex::decode("1122334455").unwrap()),
),
]
}

fn into_lotus_json(self) -> Self::LotusJson {
EAMCreateExternalParamsLotusJson(RawBytes::new(self.0))
}

fn from_lotus_json(lotus_json: Self::LotusJson) -> Self {
Self(lotus_json.0.into())
}
}
}
)+
};
}

impl_eam_create_params!(10, 11, 12, 13, 14, 15, 16);
impl_eam_create2_params!(10, 11, 12, 13, 14, 15, 16);
impl_eam_create_external_params!(10, 11, 12, 13, 14, 15, 16);
1 change: 1 addition & 0 deletions src/lotus_json/actor_states/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod account_authenticate_params;
mod account_constructor_params;
mod cron_actor_params;
mod datacap_actor_params;
mod eam_params;
mod evm_actor_params;
mod init_constructor_params;
mod init_exec4_params;
Expand Down
38 changes: 38 additions & 0 deletions src/rpc/registry/actors/eam.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2019-2025 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use crate::rpc::registry::methods_reg::{MethodRegistry, register_actor_methods};
use crate::shim::message::MethodNum;
use cid::Cid;

macro_rules! register_eam_reg_version {
($registry:expr, $code_cid:expr, $state_version:path) => {{
use $state_version::{Create2Params, CreateExternalParams, CreateParams, Method};

// Constructor has no parameters
register_actor_methods!($registry, $code_cid, [(Method::Constructor, empty),]);

register_actor_methods!(
$registry,
$code_cid,
[
(Method::Create, CreateParams),
(Method::Create2, Create2Params),
(Method::CreateExternal, CreateExternalParams),
]
);
}};
}

pub(crate) fn register_actor_methods(registry: &mut MethodRegistry, cid: Cid, version: u64) {
match version {
10 => register_eam_reg_version!(registry, cid, fil_actor_eam_state::v10),
11 => register_eam_reg_version!(registry, cid, fil_actor_eam_state::v11),
12 => register_eam_reg_version!(registry, cid, fil_actor_eam_state::v12),
13 => register_eam_reg_version!(registry, cid, fil_actor_eam_state::v13),
14 => register_eam_reg_version!(registry, cid, fil_actor_eam_state::v14),
15 => register_eam_reg_version!(registry, cid, fil_actor_eam_state::v15),
16 => register_eam_reg_version!(registry, cid, fil_actor_eam_state::v16),
_ => {}
}
}
1 change: 1 addition & 0 deletions src/rpc/registry/actors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
pub(crate) mod account;
pub(crate) mod cron;
pub(crate) mod datacap;
pub(crate) mod eam;
pub(crate) mod eth_account;
pub(crate) mod evm;
pub(crate) mod init;
Expand Down
3 changes: 2 additions & 1 deletion src/rpc/registry/methods_reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl MethodRegistry {

fn register_known_methods(&mut self) {
use crate::rpc::registry::actors::{
account, cron, datacap, eth_account, evm, init, miner, multisig, payment_channel,
account, cron, datacap, eam, eth_account, evm, init, miner, multisig, payment_channel,
power, reward, system, verified_reg,
};

Expand All @@ -101,6 +101,7 @@ impl MethodRegistry {
BuiltinActor::PaymentChannel => {
payment_channel::register_actor_methods(self, cid, version)
}
BuiltinActor::EAM => eam::register_actor_methods(self, cid, version),
_ => {}
}
}
Expand Down
43 changes: 43 additions & 0 deletions src/tool/subcommands/api_cmd/api_compare_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1903,6 +1903,7 @@ fn state_decode_params_api_tests(tipset: &Tipset) -> anyhow::Result<Vec<RpcTest>
tests.extend(multisig_actor_state_decode_params_tests(tipset)?);
tests.extend(verified_reg_actor_state_decode_params_tests(tipset)?);
tests.extend(paych_actor_state_decode_params_tests(tipset)?);
tests.extend(eam_actor_state_decode_params_tests(tipset)?);

Ok(tests)
}
Expand Down Expand Up @@ -1966,6 +1967,48 @@ fn paych_actor_state_decode_params_tests(tipset: &Tipset) -> anyhow::Result<Vec<
])
}

fn eam_actor_state_decode_params_tests(tipset: &Tipset) -> anyhow::Result<Vec<RpcTest>> {
let create_params = fil_actor_eam_state::v16::CreateParams {
initcode: vec![0x11, 0x22, 0x33, 0x44, 0x55], // dummy data
nonce: 2,
};

let create_params2 = fil_actor_eam_state::v16::Create2Params {
initcode: vec![0x11, 0x22, 0x33, 0x44, 0x55], // dummy data
salt: [0; 32],
};

let create_external_params =
fil_actor_eam_state::v16::CreateExternalParams(vec![0x11, 0x22, 0x33, 0x44, 0x55]);

Ok(vec![
RpcTest::identity(StateDecodeParams::request((
Address::ETHEREUM_ACCOUNT_MANAGER_ACTOR,
fil_actor_eam_state::v16::Method::Constructor as u64,
vec![],
tipset.key().into(),
))?),
RpcTest::identity(StateDecodeParams::request((
Address::ETHEREUM_ACCOUNT_MANAGER_ACTOR,
fil_actor_eam_state::v16::Method::Create as u64,
to_vec(&create_params)?,
tipset.key().into(),
))?),
RpcTest::identity(StateDecodeParams::request((
Address::ETHEREUM_ACCOUNT_MANAGER_ACTOR,
fil_actor_eam_state::v16::Method::Create2 as u64,
to_vec(&create_params2)?,
tipset.key().into(),
))?),
RpcTest::identity(StateDecodeParams::request((
Address::ETHEREUM_ACCOUNT_MANAGER_ACTOR,
fil_actor_eam_state::v16::Method::CreateExternal as u64,
to_vec(&create_external_params)?,
tipset.key().into(),
))?),
])
}

fn evm_actor_state_decode_params_tests(tipset: &Tipset) -> anyhow::Result<Vec<RpcTest>> {
let evm_constructor_params = fil_actor_evm_state::v16::ConstructorParams {
creator: fil_actor_evm_state::evm_shared::v16::address::EthAddress([0; 20]),
Expand Down
4 changes: 4 additions & 0 deletions src/tool/subcommands/api_cmd/test_snapshots.txt
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ filecoin_payment_channel_statedecodeparams_1756194613312797.rpcsnap.json.zst
filecoin_payment_channel_statedecodeparams_1756194613312868.rpcsnap.json.zst
filecoin_payment_channel_statedecodeparams_1756194613312941.rpcsnap.json.zst
filecoin_payment_channel_statedecodeparams_1756194613313007.rpcsnap.json.zst
filecoin_eam_statedecodeparams_1756139121347152.rpcsnap.json.zst
filecoin_eam_statedecodeparams_1756139121347218.rpcsnap.json.zst
filecoin_eam_statedecodeparams_1756139121347288.rpcsnap.json.zst
filecoin_eam_statedecodeparams_1756139121347364.rpcsnap.json.zst
filecoin_statereplay_1743504051038215.rpcsnap.json.zst
filecoin_statesearchmsg_1741784596636715.rpcsnap.json.zst
filecoin_statesearchmsglimited_1741784596704876.rpcsnap.json.zst
Expand Down
Loading