Skip to content

Commit

Permalink
feat(eam): add EAM#UpdateDeployers. (#823)
Browse files Browse the repository at this point in the history
  • Loading branch information
raulk authored Mar 19, 2024
1 parent c49b0e7 commit ffbff14
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions fendermint/actors/eam/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
148 changes: 147 additions & 1 deletion fendermint/actors/eam/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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.
Expand Down Expand Up @@ -59,6 +68,33 @@ impl IPCEamActor {

Ok(())
}

fn update_deployers(rt: &impl Runtime, deployers: Vec<Address>) -> 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 {
Expand All @@ -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)
Expand Down Expand Up @@ -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<Address>) -> MockRuntime {
let rt = MockRuntime {
Expand Down Expand Up @@ -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, &eth.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::<IPCEamActor>(
Method::CreateExternal as u64,
IpldBlock::serialize_cbor(&create_params).unwrap(),
);
assert_eq!(ExitCode::USR_FORBIDDEN, ret.err().unwrap().exit_code());

// Now add permissions for the deployer from the allowed address.
let update_deployers_params = vec![deployer.f410];
rt.set_caller(*ETHACCOUNT_ACTOR_CODE_ID, allowed.id);
let ret = rt.call::<IPCEamActor>(
ExtraMethods::UpdateDeployers as MethodNum,
IpldBlock::serialize_cbor(&update_deployers_params).unwrap(),
);
assert!(ret.is_ok());

// Previously allowed deployer no longer allowed.
rt.set_caller(*ETHACCOUNT_ACTOR_CODE_ID, allowed.id);
let ret = rt.call::<IPCEamActor>(
Method::CreateExternal as u64,
IpldBlock::serialize_cbor(&create_params).unwrap(),
);
assert_eq!(ExitCode::USR_FORBIDDEN, ret.err().unwrap().exit_code());

// New deployer is permissioned.
rt.set_caller(*ETHACCOUNT_ACTOR_CODE_ID, deployer.id);
rt.set_origin(deployer.id);
rt.expect_validate_caller_addr(vec![deployer.id]);
let send_return = IpldBlock::serialize_cbor(&Exec4Return {
id_address: Address::new_id(111),
robust_address: Address::new_id(0), // nobody cares
})
.unwrap();

let new_eth_addr = compute_address_create(&rt, &deployer.eth, 0);
let params = {
let evm_params = ConstructorParams {
creator: deployer.eth,
initcode: initcode.clone().into(),
};
Exec4Params {
code_cid: *EVM_ACTOR_CODE_ID,
constructor_params: RawBytes::serialize(evm_params).unwrap(),
subaddress: new_eth_addr.0[..].to_owned().into(),
}
};

rt.expect_send_simple(
INIT_ACTOR_ADDR,
EXEC4_METHOD,
IpldBlock::serialize_cbor(&params).unwrap(),
TokenAmount::from_atto(0),
send_return,
ExitCode::OK,
);

let ret = rt
.call::<IPCEamActor>(
Method::CreateExternal as u64,
IpldBlock::serialize_cbor(&create_params).unwrap(),
)
.unwrap()
.unwrap()
.deserialize::<Return>()
.unwrap();

let expected_return = Return {
actor_id: 111,
robust_address: Some(Address::new_id(0)),
eth_address: new_eth_addr,
};

assert_eq!(ret, expected_return);
}
}
4 changes: 2 additions & 2 deletions fendermint/actors/eam/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub enum PermissionModeParams {

/// The permission mode for controlling who can deploy contracts
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
enum PermissionMode {
pub enum PermissionMode {
/// No restriction, everyone can deploy
Unrestricted,
/// Only whitelisted addresses can deploy
Expand All @@ -32,7 +32,7 @@ enum PermissionMode {

#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)]
pub struct State {
permission_mode: PermissionMode,
pub permission_mode: PermissionMode,
}

impl State {
Expand Down

0 comments on commit ffbff14

Please sign in to comment.