diff --git a/Cargo.lock b/Cargo.lock index 2ccdfad7f..368f47936 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2811,6 +2811,7 @@ dependencies = [ "fil_actor_eam", "fil_actors_evm_shared", "fil_actors_runtime", + "frc42_dispatch", "fvm_ipld_blockstore", "fvm_ipld_encoding", "fvm_shared", diff --git a/fendermint/actors/eam/Cargo.toml b/fendermint/actors/eam/Cargo.toml index 0dba94b4c..859a87530 100644 --- a/fendermint/actors/eam/Cargo.toml +++ b/fendermint/actors/eam/Cargo.toml @@ -25,6 +25,7 @@ num-derive = { workspace = true } num-traits = { workspace = true } serde = { workspace = true } hex-literal = { workspace = true } +frc42_dispatch = { workspace = true } [dev-dependencies] fil_actors_evm_shared = { workspace = true } diff --git a/fendermint/actors/eam/src/lib.rs b/fendermint/actors/eam/src/lib.rs index 9ebbb1c78..82456dc31 100644 --- a/fendermint/actors/eam/src/lib.rs +++ b/fendermint/actors/eam/src/lib.rs @@ -9,8 +9,11 @@ use fil_actors_runtime::EAM_ACTOR_ID; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::tuple::*; +use fvm_shared::address::Address; use fvm_shared::{ActorID, MethodNum}; +use num_derive::FromPrimitive; +use crate::state::PermissionMode; pub use crate::state::PermissionModeParams; pub use crate::state::State; @@ -24,6 +27,12 @@ pub const IPC_EAM_ACTOR_ID: ActorID = EAM_ACTOR_ID; pub struct IPCEamActor; +#[derive(FromPrimitive)] +#[repr(u64)] +pub enum ExtraMethods { + UpdateDeployers = frc42_dispatch::method_hash!("UpdateDeployers"), +} + impl IPCEamActor { /// Creates the actor. If the `whitelisted_deployers` is empty, that means there is no restriction /// for deployment, i.e any address can deploy. @@ -59,6 +68,33 @@ impl IPCEamActor { Ok(()) } + + fn update_deployers(rt: &impl Runtime, deployers: Vec
) -> Result<(), ActorError> { + // Reject update if we're unrestricted. + let state: State = rt.state()?; + if !matches!(state.permission_mode, PermissionMode::AllowList(_)) { + return Err(ActorError::forbidden(String::from( + "deployers can only be updated in allowlist mode", + ))); + }; + + // Check that the caller is in the allowlist. + let caller_id = rt.message().caller().id().unwrap(); + if !state.can_deploy(rt, caller_id)? { + return Err(ActorError::forbidden(String::from( + "sender not allowed to update deployers", + ))); + } + + // Perform the update. + rt.transaction(|st: &mut State, rt| { + st.permission_mode = + State::new(rt.store(), PermissionModeParams::AllowList(deployers))?.permission_mode; + Ok(()) + })?; + + Ok(()) + } } impl ActorCode for IPCEamActor { @@ -79,6 +115,8 @@ impl ActorCode for IPCEamActor { { if method == Method::Constructor as u64 { fil_actors_runtime::dispatch(rt, method, Self::constructor, params) + } else if method == ExtraMethods::UpdateDeployers as u64 { + fil_actors_runtime::dispatch(rt, method, Self::update_deployers, params) } else { Self::ensure_deployer_allowed(rt)?; EamActor::invoke_method(rt, method, params) @@ -109,9 +147,10 @@ mod tests { use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; + use fvm_shared::MethodNum; use crate::state::PermissionModeParams; - use crate::{ConstructorParams as IPCConstructorParams, IPCEamActor, Method}; + use crate::{ConstructorParams as IPCConstructorParams, ExtraMethods, IPCEamActor, Method}; pub fn construct_and_verify(deployers: Vec) -> MockRuntime { let rt = MockRuntime { @@ -372,4 +411,111 @@ mod tests { assert_eq!(result, expected_return); rt.verify(); } + + #[test] + fn test_update_deployers() { + let deployers = vec![Address::new_id(1000)]; + let rt = construct_and_verify(deployers); + + struct AddrTriple { + eth: EthAddress, + f410: Address, + id: Address, + } + + macro_rules! create_address { + ($hex_addr:expr, $id:expr) => {{ + let eth = EthAddress(hex_literal::hex!($hex_addr)); + let f410 = Address::new_delegated(10, ð.0).unwrap(); + rt.set_delegated_address($id, f410); + AddrTriple { + eth, + f410, + id: Address::new_id($id), + } + }}; + } + + let allowed = create_address!("CAFEB0BA00000000000000000000000000000000", 1000); + let deployer = create_address!("FAAAB0BA00000000000000000000000000000000", 2000); + + let initcode = vec![0xff]; + + let create_params = CreateExternalParams(initcode.clone()); + + // Deployer is not allowed to create yet. + rt.set_caller(*ETHACCOUNT_ACTOR_CODE_ID, deployer.id); + let ret = rt.call::