Skip to content

Commit

Permalink
Init actor implementation (#282)
Browse files Browse the repository at this point in the history
* Implement init actor (todo state)

* Implement init actor state and fix missing init store in state tree

* Switch error type

* Swap errors
  • Loading branch information
austinabell authored Mar 16, 2020
1 parent 5d82917 commit 7c8fb8c
Show file tree
Hide file tree
Showing 14 changed files with 294 additions and 70 deletions.
6 changes: 6 additions & 0 deletions ipld/hamt/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ impl fmt::Display for Error {
}
}

impl From<Error> for String {
fn from(e: Error) -> Self {
e.to_string()
}
}

impl From<DBError> for Error {
fn from(e: DBError) -> Error {
Error::Db(e.to_string())
Expand Down
4 changes: 3 additions & 1 deletion ipld/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod ser;
pub use self::error::Error;

use cid::Cid;
use encoding::{from_slice, to_vec};
use encoding::{from_slice, to_vec, Cbor};
use ser::Serializer;
use serde::de::DeserializeOwned;
use serde::Serialize;
Expand All @@ -28,6 +28,8 @@ pub enum Ipld {
Link(Cid),
}

impl Cbor for Ipld {}

/// Convert any object into an IPLD object
pub fn to_ipld<T>(ipld: T) -> Result<Ipld, Error>
where
Expand Down
2 changes: 2 additions & 0 deletions vm/actor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ libp2p = "0.15.0"
lazy_static = "1.4.0"
ipld_blockstore = { path = "../../ipld/blockstore" }
ipld_hamt = { path = "../../ipld/hamt" }
forest_ipld = { path = "../../ipld" }
message = { package = "forest_message", path = "../message" }
25 changes: 25 additions & 0 deletions vm/actor/src/builtin/codes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use cid::{multihash::Identity, Cid, Codec, Version};

lazy_static! {
pub static ref SYSTEM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/system");
pub static ref INIT_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/init");
pub static ref CRON_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/cron");
pub static ref ACCOUNT_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/account");
pub static ref POWER_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/storagepower");
pub static ref MINER_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/storageminer");
pub static ref MARKET_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/storagemarket");
pub static ref PAYCH_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/paymentchannel");
pub static ref MULTISIG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/multisig");
pub static ref REWARD_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/reward");

// Set of actor code types that can represent external signing parties.
pub static ref CALLER_TYPES_SIGNABLE: [Cid; 2] =
[ACCOUNT_ACTOR_CODE_ID.clone(), MULTISIG_ACTOR_CODE_ID.clone()];
}

fn make_builtin(bz: &[u8]) -> Cid {
Cid::new(Codec::Raw, Version::V1, Identity::digest(bz))
}
125 changes: 95 additions & 30 deletions vm/actor/src/builtin/init/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

mod params;
mod state;

pub use self::params::*;
pub use self::state::State;
use crate::empty_return;
use crate::{
empty_return, make_map, ACCOUNT_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID, MARKET_ACTOR_CODE_ID,
MINER_ACTOR_CODE_ID, POWER_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR,
};
use address::Address;
use cid::Cid;
use forest_ipld::Ipld;
use ipld_blockstore::BlockStore;
use message::Message;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use runtime::{ActorCode, Runtime};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use vm::{ExitCode, MethodNum, Serialized, METHOD_CONSTRUCTOR};

/// Init actor methods available
Expand All @@ -26,32 +34,88 @@ impl Method {
}
}

/// Constructor parameters
pub struct ConstructorParams {
pub network_name: String,
}

/// Init actor
pub struct Actor;
impl Actor {
/// Init actor constructor
pub fn constructor<BS, RT>(_rt: &RT, _params: ConstructorParams)
pub fn constructor<BS, RT>(rt: &RT, params: ConstructorParams)
where
BS: BlockStore,
RT: Runtime<BS>,
{
// TODO
todo!()
let sys_ref: &Address = &SYSTEM_ACTOR_ADDR;
rt.validate_immediate_caller_is(std::iter::once(sys_ref));
let mut empty_map = make_map(rt.store());
let root = match empty_map.flush() {
Ok(cid) => cid,
Err(e) => {
rt.abort(
ExitCode::ErrIllegalState,
format!("failed to construct state: {}", e),
);
unreachable!()
}
};
rt.create(&State::new(root, params.network_name));
}

/// Exec init actor
pub fn exec<BS, RT>(_rt: &RT, _params: &Serialized) -> Serialized
pub fn exec<BS, RT>(rt: &RT, params: ExecParams) -> ExecReturn
where
BS: BlockStore,
RT: Runtime<BS>,
{
// TODO update and include exec params type and return
todo!()
rt.validate_immediate_caller_accept_any();
let caller_code = rt
.get_actor_code_cid(rt.message().from())
.expect("no code for actor");
if !can_exec(&caller_code, &params.code_cid) {
rt.abort(
ExitCode::ErrForbidden,
format!(
"called type {} cannot exec actor type {}",
&caller_code, &params.code_cid
),
)
}

// Compute a re-org-stable address.
// This address exists for use by messages coming from outside the system, in order to
// stably address the newly created actor even if a chain re-org causes it to end up with
// a different ID.
let robust_address = rt.new_actor_address();

// Allocate an ID for this actor.
// Store mapping of pubkey or actor address to actor ID
let id_address: Address = rt.transaction::<State, _, _>(|s| {
match s.map_address_to_new_id(rt.store(), &robust_address) {
Ok(a) => a,
Err(e) => {
rt.abort(ExitCode::ErrIllegalState, format!("exec failed {}", e));
unreachable!()
}
}
});

// Create an empty actor
rt.create_actor(&params.code_cid, &id_address);

// Invoke constructor
let (_, exit_code) = rt.send::<Ipld>(
&id_address,
MethodNum::new(METHOD_CONSTRUCTOR as u64),
&params.constructor_params,
rt.message().value(),
);

if !exit_code.is_success() {
rt.abort(exit_code, "constructor failed".to_owned());
}

ExecReturn {
id_address,
robust_address,
}
}
}

Expand All @@ -67,8 +131,7 @@ impl ActorCode for Actor {
empty_return()
}
Some(Method::Exec) => {
// TODO update to correct param type
Self::exec(rt, params)
Serialized::serialize(Self::exec(rt, params.deserialize().unwrap())).unwrap()
}
_ => {
// Method number does not match available, abort in runtime
Expand All @@ -79,21 +142,23 @@ impl ActorCode for Actor {
}
}

impl Serialize for ConstructorParams {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
[&self.network_name].serialize(serializer)
}
}

impl<'de> Deserialize<'de> for ConstructorParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
fn can_exec(caller: &Cid, exec: &Cid) -> bool {
let (acc_ref, init_ref, power_ref, mar_ref, miner_ref): (&Cid, &Cid, &Cid, &Cid, &Cid) = (
&ACCOUNT_ACTOR_CODE_ID,
&INIT_ACTOR_CODE_ID,
&POWER_ACTOR_CODE_ID,
&MARKET_ACTOR_CODE_ID,
&MINER_ACTOR_CODE_ID,
);
// TODO spec also checks for an undefined Cid, see if this should be supported
if exec == acc_ref
|| exec == init_ref
|| exec == power_ref
|| exec == mar_ref
|| exec == miner_ref
{
let [network_name]: [String; 1] = Deserialize::deserialize(deserializer)?;
Ok(Self { network_name })
exec == miner_ref && caller == power_ref
} else {
true
}
}
89 changes: 89 additions & 0 deletions vm/actor/src/builtin/init/params.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use address::Address;
use cid::Cid;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use vm::Serialized;

/// Constructor parameters
pub struct ConstructorParams {
pub network_name: String,
}

impl Serialize for ConstructorParams {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
[&self.network_name].serialize(serializer)
}
}

impl<'de> Deserialize<'de> for ConstructorParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let [network_name]: [String; 1] = Deserialize::deserialize(deserializer)?;
Ok(Self { network_name })
}
}

/// Exec Params
pub struct ExecParams {
pub code_cid: Cid,
pub constructor_params: Serialized,
}

impl Serialize for ExecParams {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
(&self.code_cid, &self.constructor_params).serialize(serializer)
}
}

impl<'de> Deserialize<'de> for ExecParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let (code_cid, constructor_params) = Deserialize::deserialize(deserializer)?;
Ok(Self {
code_cid,
constructor_params,
})
}
}

/// Exec Return value
pub struct ExecReturn {
/// ID based address for created actor
pub id_address: Address,
/// Reorg safe address for actor
pub robust_address: Address,
}

impl Serialize for ExecReturn {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
(&self.id_address, &self.robust_address).serialize(serializer)
}
}

impl<'de> Deserialize<'de> for ExecReturn {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let (id_address, robust_address) = Deserialize::deserialize(deserializer)?;
Ok(Self {
id_address,
robust_address,
})
}
}
Loading

0 comments on commit 7c8fb8c

Please sign in to comment.