diff --git a/Cargo.lock b/Cargo.lock index 276a0f7f7fa3..a653340c7f93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2529,6 +2529,7 @@ dependencies = [ "forest_bigint", "forest_cid", "forest_encoding", + "lazy_static", "num-derive", "num-traits 0.2.12", "serde", diff --git a/blockchain/state_manager/src/lib.rs b/blockchain/state_manager/src/lib.rs index 84eaf74ae16c..0f95b605b241 100644 --- a/blockchain/state_manager/src/lib.rs +++ b/blockchain/state_manager/src/lib.rs @@ -18,6 +18,7 @@ use cid::Cid; use clock::ChainEpoch; use encoding::de::DeserializeOwned; use encoding::Cbor; +use fil_types::DevnetParams; use flo_stream::Subscriber; use forest_blocks::{Block, BlockHeader, FullTipset, Tipset, TipsetKeys}; use futures::channel::oneshot; @@ -163,7 +164,8 @@ where ) -> Result<(Cid, Cid), Box> { let mut buf_store = BufferedBlockStore::new(self.bs.as_ref()); // TODO possibly switch out syscalls to be saved at state manager level - let mut vm = VM::new( + // TODO change from statically using devnet params when needed + let mut vm = VM::<_, _, DevnetParams>::new( ts.parent_state(), &buf_store, ts.epoch(), @@ -172,7 +174,7 @@ where )?; // Apply tipset messages - let receipts = vm.apply_tip_set_messages(ts, callback)?; + let receipts = vm.apply_tipset_messages(ts, callback)?; // Construct receipt root from receipts let rect_root = Amt::new_from_slice(self.bs.as_ref(), &receipts)?; @@ -238,7 +240,7 @@ where span!("state_call_raw", { let block_store = self.get_block_store_ref(); let buf_store = BufferedBlockStore::new(block_store); - let mut vm = VM::new( + let mut vm = VM::<_, _, DevnetParams>::new( bstate, &buf_store, *bheight, @@ -261,14 +263,14 @@ where msg.gas_price(), msg.value() ); - if let Some(err) = apply_ret.act_error() { + if let Some(err) = &apply_ret.act_error { warn!("chain call failed: {:?}", err); } Ok(InvocResult { msg: msg.clone(), - msg_rct: Some(apply_ret.msg_receipt().clone()), - actor_error: apply_ret.act_error().map(|e| e.to_string()), + msg_rct: Some(apply_ret.msg_receipt.clone()), + actor_error: apply_ret.act_error.map(|e| e.to_string()), }) }) } @@ -672,7 +674,7 @@ where Ok(actor.balance) } - pub fn lookup_id(&self, addr: &Address, ts: &Tipset) -> Result { + pub fn lookup_id(&self, addr: &Address, ts: &Tipset) -> Result, Error> { let state_tree = StateTree::new_from_root(self.bs.as_ref(), ts.parent_state())?; state_tree.lookup_id(addr).map_err(Error::State) } @@ -681,7 +683,9 @@ where let market_state: market::State = self.load_actor_state(&*STORAGE_MARKET_ACTOR_ADDR, ts.parent_state())?; - let new_addr = self.lookup_id(addr, ts)?; + let new_addr = self + .lookup_id(addr, ts)? + .ok_or_else(|| Error::State(format!("Failed to resolve address {}", addr)))?; let out = MarketBalance { escrow: { diff --git a/node/state_api/src/lib.rs b/node/state_api/src/lib.rs index c1d14eeb9195..48d90b1d7b03 100644 --- a/node/state_api/src/lib.rs +++ b/node/state_api/src/lib.rs @@ -256,9 +256,9 @@ where Ok(InvocResult { msg, - msg_rct: ret.clone().map(|s| s.msg_receipt().clone()), + msg_rct: ret.as_ref().map(|s| s.msg_receipt.clone()), actor_error: ret - .map(|act| act.act_error().map(|e| e.to_string())) + .map(|act| act.act_error.map(|e| e.to_string())) .unwrap_or_default(), }) } @@ -323,7 +323,7 @@ pub fn state_lookup_id( state_manager: &StateManager, address: &Address, key: &TipsetKeys, -) -> Result +) -> Result, BoxError> where DB: BlockStore, { diff --git a/types/src/lib.rs b/types/src/lib.rs index 82cc05c98bc9..3c9ef91204cc 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -6,3 +6,34 @@ pub mod sector; pub use self::piece::*; pub use self::sector::*; + +use num_bigint::BigInt; + +/// Config trait which handles different network configurations. +pub trait NetworkParams { + /// Total filecoin available to network. + const TOTAL_FILECOIN: i64; + + /// Available rewards for mining. + const MINING_REWARD_TOTAL: i64; + + /// Initial reward actor balance. This function is only called in genesis setting up state. + fn initial_reward_balance() -> BigInt { + BigInt::from(Self::MINING_REWARD_TOTAL) * Self::TOTAL_FILECOIN + } + + /// Convert integer value of tokens into BigInt based on the token precision. + fn from_fil(i: i64) -> BigInt { + BigInt::from(i) * FILECOIN_PRECISION + } +} + +// Not yet finalized +pub struct DevnetParams; +impl NetworkParams for DevnetParams { + const TOTAL_FILECOIN: i64 = 2_000_000_000; + const MINING_REWARD_TOTAL: i64 = 1_400_000_000; +} + +/// Ratio of integer values to token value. +pub const FILECOIN_PRECISION: i64 = 1_000_000_000_000_000_000; diff --git a/vm/Cargo.toml b/vm/Cargo.toml index d69a3a3b159a..93c76dfda35e 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -19,6 +19,7 @@ cid = { package = "forest_cid", path = "../ipld/cid", version = "0.1", features num-traits = "0.2" num-derive = "0.3.0" thiserror = "1.0.11" +lazy_static = "1.4" [features] json = [] \ No newline at end of file diff --git a/vm/actor/src/builtin/account/mod.rs b/vm/actor/src/builtin/account/mod.rs index 9c3bfb3b8730..fe136ff77dfc 100644 --- a/vm/actor/src/builtin/account/mod.rs +++ b/vm/actor/src/builtin/account/mod.rs @@ -44,12 +44,12 @@ impl Actor { } // Fetches the pubkey-type address from this actor. - pub fn pubkey_address(rt: &RT) -> Result + pub fn pubkey_address(rt: &mut RT) -> Result where BS: BlockStore, RT: Runtime, { - rt.validate_immediate_caller_accept_any(); + rt.validate_immediate_caller_accept_any()?; let st: State = rt.state()?; Ok(st.address) } diff --git a/vm/actor/src/builtin/codes.rs b/vm/actor/src/builtin/codes.rs index b3d463b05282..bdeac5982cec 100644 --- a/vm/actor/src/builtin/codes.rs +++ b/vm/actor/src/builtin/codes.rs @@ -14,7 +14,7 @@ lazy_static! { 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"); - pub static ref VERIFIED_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/verifiedregistry"); + pub static ref VERIFREG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/verifiedregistry"); // Set of actor code types that can represent external signing parties. pub static ref CALLER_TYPES_SIGNABLE: [Cid; 2] = diff --git a/vm/actor/src/builtin/cron/mod.rs b/vm/actor/src/builtin/cron/mod.rs index 4f874394139d..d8a0db5419ca 100644 --- a/vm/actor/src/builtin/cron/mod.rs +++ b/vm/actor/src/builtin/cron/mod.rs @@ -57,10 +57,10 @@ impl Actor { let st: State = rt.state()?; for entry in st.entries { let _v = rt.send( - &entry.receiver, + entry.receiver, entry.method_num, - &Serialized::default(), - &TokenAmount::from(0u8), + Serialized::default(), + TokenAmount::from(0u8), ); } Ok(()) diff --git a/vm/actor/src/builtin/init/mod.rs b/vm/actor/src/builtin/init/mod.rs index 583f3ffb4759..bd95b327e960 100644 --- a/vm/actor/src/builtin/init/mod.rs +++ b/vm/actor/src/builtin/init/mod.rs @@ -16,7 +16,7 @@ use ipld_blockstore::BlockStore; use num_derive::FromPrimitive; use num_traits::FromPrimitive; use runtime::{ActorCode, Runtime}; -use vm::{ActorError, ExitCode, MethodNum, Serialized, METHOD_CONSTRUCTOR}; +use vm::{actor_error, ActorError, ExitCode, MethodNum, Serialized, METHOD_CONSTRUCTOR}; /// Init actor methods available #[derive(FromPrimitive)] @@ -56,17 +56,14 @@ impl Actor { BS: BlockStore, RT: Runtime, { - rt.validate_immediate_caller_accept_any(); + rt.validate_immediate_caller_accept_any()?; let caller_code = rt - .get_actor_code_cid(rt.message().caller()) - .expect("no code for actor"); + .get_actor_code_cid(rt.message().caller())? + .ok_or_else(|| actor_error!(ErrForbidden; "No code for actor"))?; if !can_exec(&caller_code, ¶ms.code_cid) { - return Err(rt.abort( - ExitCode::ErrForbidden, - format!( + return Err(actor_error!(ErrForbidden; "called type {} cannot exec actor type {}", &caller_code, ¶ms.code_cid - ), )); } @@ -90,10 +87,10 @@ impl Actor { // Invoke constructor rt.send( - &id_address, + id_address, METHOD_CONSTRUCTOR, - ¶ms.constructor_params, - &rt.message().value_received().clone(), + params.constructor_params, + rt.message().value_received().clone(), ) .map_err(|err| rt.abort(err.exit_code(), "constructor failed"))?; diff --git a/vm/actor/src/builtin/init/state.rs b/vm/actor/src/builtin/init/state.rs index 1ac2ca4004b3..e35c6dba320a 100644 --- a/vm/actor/src/builtin/init/state.rs +++ b/vm/actor/src/builtin/init/state.rs @@ -55,19 +55,17 @@ impl State { &self, store: &BS, addr: &Address, - ) -> Result { + ) -> Result, String> { if addr.protocol() == Protocol::ID { - return Ok(*addr); + return Ok(Some(*addr)); } let map: Hamt = Hamt::load_with_bit_width(&self.address_map, store, HAMT_BIT_WIDTH)?; - let actor_id: ActorID = map - .get(&addr.to_bytes())? - .ok_or_else(|| "Address not found".to_owned())?; - - Ok(Address::new_id(actor_id)) + Ok(map + .get::<_, ActorID>(&addr.to_bytes())? + .map(Address::new_id)) } } diff --git a/vm/actor/src/builtin/market/mod.rs b/vm/actor/src/builtin/market/mod.rs index dfe3fabcd18a..387f70de98e2 100644 --- a/vm/actor/src/builtin/market/mod.rs +++ b/vm/actor/src/builtin/market/mod.rs @@ -28,7 +28,8 @@ use num_derive::FromPrimitive; use num_traits::{FromPrimitive, Zero}; use runtime::{ActorCode, Runtime}; use vm::{ - ActorError, ExitCode, MethodNum, Serialized, TokenAmount, METHOD_CONSTRUCTOR, METHOD_SEND, + actor_error, ActorError, ExitCode, MethodNum, Serialized, TokenAmount, METHOD_CONSTRUCTOR, + METHOD_SEND, }; /// Market actor methods available @@ -154,17 +155,17 @@ impl Actor { // TODO this will never be hit if amount_slashed_total > BigInt::zero() { rt.send( - &*BURNT_FUNDS_ACTOR_ADDR, + *BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, - &Serialized::default(), - &amount_slashed_total, + Serialized::default(), + amount_slashed_total, )?; } rt.send( - &recipient, + recipient, METHOD_SEND, - &Serialized::default(), - &amount_extracted, + Serialized::default(), + amount_extracted, )?; Ok(()) } @@ -189,10 +190,12 @@ impl Actor { } // All deals should have the same provider so get worker once - let provider_raw = ¶ms.deals[0].proposal.provider; - let provider = rt.resolve_address(&provider_raw)?; + let provider_raw = params.deals[0].proposal.provider; + let provider = rt.resolve_address(&provider_raw)?.ok_or_else( + || actor_error!(ErrNotFound; "failed to resolve provider address {}", provider_raw), + )?; - let (_, worker) = request_miner_control_addrs(rt, &provider)?; + let (_, worker) = request_miner_control_addrs(rt, provider)?; if &worker != rt.message().caller() { return Err(ActorError::new( ExitCode::ErrForbidden, @@ -210,18 +213,14 @@ impl Actor { deal_size: BigInt::from(deal.proposal.piece_size.0), })?; rt.send( - &*VERIFIED_REGISTRY_ACTOR_ADDR, + *VERIFIED_REGISTRY_ACTOR_ADDR, VerifregMethod::UseBytes as u64, - &ser_params, - &TokenAmount::zero(), + ser_params, + TokenAmount::zero(), )?; } } - // All deals should have the same provider so get worker once - let provider_raw = params.deals[0].proposal.provider; - let provider = rt.resolve_address(&provider_raw)?; - let mut new_deal_ids: Vec = Vec::new(); rt.transaction(|st: &mut State, rt| { let mut prop = Amt::load(&st.proposals, rt.store()) @@ -240,7 +239,10 @@ impl Actor { )); } - let client = rt.resolve_address(&deal.proposal.client)?; + let client = rt.resolve_address(&deal.proposal.client)?.ok_or_else(|| { + actor_error!(ErrNotFound; + "failed to resolve provider address {}", provider_raw) + })?; // Normalise provider and client addresses in the proposal stored on chain (after signature verification). deal.proposal.provider = provider; deal.proposal.client = client; @@ -615,18 +617,18 @@ impl Actor { deal_size: BigInt::from(d.piece_size.0), })?; rt.send( - &*VERIFIED_REGISTRY_ACTOR_ADDR, + *VERIFIED_REGISTRY_ACTOR_ADDR, VerifregMethod::RestoreBytes as u64, - &ser_params, - &TokenAmount::zero(), + ser_params, + TokenAmount::zero(), )?; } rt.send( - &*BURNT_FUNDS_ACTOR_ADDR, + *BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, - &Serialized::default(), - &amount_slashed, + Serialized::default(), + amount_slashed, )?; Ok(()) } @@ -768,19 +770,13 @@ where RT: Runtime, { // Resolve the provided address to the canonical form against which the balance is held. - let nominal = rt.resolve_address(addr).map_err(|e| { - ActorError::new( - ExitCode::ErrIllegalArgument, - format!("Failed to resolve address provided: {}", e), - ) - })?; + let nominal = rt + .resolve_address(addr)? + .ok_or_else(|| actor_error!(ErrIllegalArgument; "failed to resolve address {}", addr))?; - let code_id = rt.get_actor_code_cid(&nominal).map_err(|e| { - ActorError::new( - ExitCode::ErrIllegalArgument, - format!("Failed to retrieve actor code cid: {}", e), - ) - })?; + let code_id = rt + .get_actor_code_cid(&nominal)? + .ok_or_else(|| actor_error!(ErrIllegalArgument; "no code for address {}", nominal))?; if code_id != *MINER_ACTOR_CODE_ID { // Ordinary account-style actor entry; funds recipient is just the entry address itself. @@ -789,7 +785,7 @@ where } // Storage miner actor entry; implied funds recipient is the associated owner address. - let (owner_addr, worker_addr) = request_miner_control_addrs(rt, &nominal)?; + let (owner_addr, worker_addr) = request_miner_control_addrs(rt, nominal)?; rt.validate_immediate_caller_is([owner_addr, worker_addr].iter())?; Ok((nominal, owner_addr)) } diff --git a/vm/actor/src/builtin/miner/mod.rs b/vm/actor/src/builtin/miner/mod.rs index 8d956112d987..861691c15a29 100644 --- a/vm/actor/src/builtin/miner/mod.rs +++ b/vm/actor/src/builtin/miner/mod.rs @@ -54,8 +54,8 @@ use std::collections::HashMap; use std::error::Error as StdError; use std::ops::Neg; use vm::{ - ActorError, DealID, ExitCode, MethodNum, Serialized, TokenAmount, METHOD_CONSTRUCTOR, - METHOD_SEND, + actor_error, ActorError, DealID, ExitCode, MethodNum, Serialized, TokenAmount, + METHOD_CONSTRUCTOR, METHOD_SEND, }; // * Updated to specs-actors commit: 9e8c0d1c40d8b41de5dc727b6791c89e14fea4a8 @@ -172,7 +172,7 @@ impl Actor { BS: BlockStore, RT: Runtime, { - rt.validate_immediate_caller_accept_any(); + rt.validate_immediate_caller_accept_any()?; let st: State = rt.state()?; Ok(GetControlAddressesReturn { owner: st.info.owner, @@ -402,7 +402,7 @@ impl Actor { })??; // Remove power for new faults, and burn penalties. request_begin_faults(rt, sec_size, &detected_faults_sector)?; - burn_funds_and_notify_pledge_change(rt, &penalty)?; + burn_funds_and_notify_pledge_change(rt, penalty)?; // restore power for recovered sectors if !recovered_sectors.is_empty() { @@ -581,7 +581,7 @@ impl Actor { BS: BlockStore, RT: Runtime, { - rt.validate_immediate_caller_accept_any(); + rt.validate_immediate_caller_accept_any()?; let sector_number = params.sector_number; let st: State = rt.state()?; @@ -641,10 +641,10 @@ impl Actor { )?; rt.send( - &*STORAGE_POWER_ACTOR_ADDR, + *STORAGE_POWER_ACTOR_ADDR, PowerMethod::SubmitPoRepForBulkVerify as u64, - &Serialized::serialize(&svi)?, - &BigInt::zero(), + Serialized::serialize(&svi)?, + BigInt::zero(), )?; Ok(()) @@ -687,10 +687,10 @@ impl Actor { // TODO revisit spec TODOs let mut ret = rt.send( - &*STORAGE_MARKET_ACTOR_ADDR, + *STORAGE_MARKET_ACTOR_ADDR, MarketMethod::VerifyDealsOnSectorProveCommit as u64, - &ser_params, - &TokenAmount::zero(), + ser_params, + TokenAmount::zero(), )?; let deal_weights: VerifyDealsOnSectorProveCommitReturn = ret.deserialize()?; @@ -705,10 +705,10 @@ impl Actor { }, })?; ret = rt.send( - &*STORAGE_POWER_ACTOR_ADDR, + *STORAGE_POWER_ACTOR_ADDR, PowerMethod::OnSectorProveCommit as u64, - ¶m, - &TokenAmount::zero(), + param, + TokenAmount::zero(), )?; let BigIntDe(initial_pledge) = ret.deserialize()?; @@ -782,7 +782,7 @@ impl Actor { BS: BlockStore, RT: Runtime, { - rt.validate_immediate_caller_accept_any(); + rt.validate_immediate_caller_accept_any()?; let st: State = rt.state()?; let sec = st.get_sector(rt.store(), params.sector_number); @@ -845,10 +845,10 @@ impl Actor { })?; rt.send( - &*STORAGE_POWER_ACTOR_ADDR, + *STORAGE_POWER_ACTOR_ADDR, PowerMethod::OnSectorModifyWeightDesc as u64, - &ser_params, - &BigInt::zero(), + ser_params, + BigInt::zero(), )?; // store new sector expiry @@ -1051,7 +1051,7 @@ impl Actor { // remove power for new faulty sectors detected_fault_sectors.append(&mut declared_fault_sectors); request_begin_faults(rt, sector_size, &detected_fault_sectors)?; - burn_funds_and_notify_pledge_change(rt, &penalty)?; + burn_funds_and_notify_pledge_change(rt, penalty)?; Ok(()) } @@ -1150,7 +1150,7 @@ impl Actor { // remove power for new faulty sectors request_begin_faults(rt, sector_size, &detected_fault_sectors)?; - burn_funds_and_notify_pledge_change(rt, &penalty)?; + burn_funds_and_notify_pledge_change(rt, penalty)?; // Power is not restored yet, but when the recovered sectors are successfully PoSted. Ok(()) @@ -1240,22 +1240,17 @@ impl Actor { let st: State = rt.state()?; rt.send( - &*STORAGE_POWER_ACTOR_ADDR, + *STORAGE_POWER_ACTOR_ADDR, PowerMethod::OnConsensusFault as u64, - &Serialized::serialize(BigIntSer(&st.locked_funds))?, - &BigInt::zero(), + Serialized::serialize(BigIntSer(&st.locked_funds))?, + BigInt::zero(), )?; // TODO: terminate deals with market actor, https://github.com/filecoin-project/specs-actors/issues/279 // Reward reporter with a share of the miner's current balance. let slasher_reward = reward_for_consensus_slash_report(fault_age, rt.current_balance()?); - rt.send( - &reporter, - METHOD_SEND, - &Serialized::default(), - &slasher_reward, - )?; + rt.send(reporter, METHOD_SEND, Serialized::default(), slasher_reward)?; // Delete the actor and burn all remaining funds rt.delete_actor(&*BURNT_FUNDS_ACTOR_ADDR)?; @@ -1297,10 +1292,10 @@ impl Actor { assert!(amount_withdrawn <= curr_balance); rt.send( - &st.info.owner, + st.info.owner, METHOD_SEND, - &Serialized::default(), - &amount_withdrawn, + Serialized::default(), + amount_withdrawn, )?; notify_pledge_change(rt, &vested_amount.neg())?; @@ -1391,7 +1386,7 @@ where // Remove power for new faults, and burn penalties. request_begin_faults(rt, sector_size, &detected_fault_sectors)?; - burn_funds_and_notify_pledge_change(rt, &penalty)?; + burn_funds_and_notify_pledge_change(rt, penalty)?; deadline }; @@ -1454,7 +1449,7 @@ where })??; terminate_sectors(rt, &expired_faults, SECTOR_TERMINATION_FAULTY)?; - burn_funds_and_notify_pledge_change(rt, &ongoing_fault_penalty)?; + burn_funds_and_notify_pledge_change(rt, ongoing_fault_penalty)?; } let proving_period_start = { @@ -1838,7 +1833,7 @@ where Ok(()) })??; // This deposit was locked separately to pledge collateral so there's no pledge change here. - burn_funds(rt, &deposit_burn)?; + burn_funds(rt, deposit_burn)?; Ok(()) } @@ -1940,7 +1935,7 @@ where request_terminate_deals(rt, deal_ids)?; request_terminate_power(rt, termination_type, state.info.sector_size, &all_sectors)?; - burn_funds_and_notify_pledge_change(rt, &penalty)?; + burn_funds_and_notify_pledge_change(rt, penalty)?; Ok(()) } @@ -1984,10 +1979,10 @@ where payload, })?; rt.send( - &*STORAGE_POWER_ACTOR_ADDR, + *STORAGE_POWER_ACTOR_ADDR, PowerMethod::EnrollCronEvent as u64, - &ser_params, - &TokenAmount::zero(), + ser_params, + TokenAmount::zero(), )?; Ok(()) @@ -2013,10 +2008,10 @@ where let ser_params = Serialized::serialize(OnFaultBeginParams { weights })?; rt.send( - &*STORAGE_POWER_ACTOR_ADDR, + *STORAGE_POWER_ACTOR_ADDR, PowerMethod::OnFaultBegin as u64, - &ser_params, - &TokenAmount::zero(), + ser_params, + TokenAmount::zero(), )?; Ok(()) } @@ -2041,10 +2036,10 @@ where let ser_params = Serialized::serialize(OnFaultEndParams { weights })?; rt.send( - &*STORAGE_POWER_ACTOR_ADDR, + *STORAGE_POWER_ACTOR_ADDR, PowerMethod::OnFaultEnd as u64, - &ser_params, - &TokenAmount::zero(), + ser_params, + TokenAmount::zero(), )?; Ok(()) } @@ -2059,10 +2054,10 @@ where } rt.send( - &*STORAGE_MARKET_ACTOR_ADDR, + *STORAGE_MARKET_ACTOR_ADDR, MarketMethod::OnMinerSectorsTerminate as u64, - &Serialized::serialize(OnMinerSectorsTerminateParams { deal_ids })?, - &TokenAmount::zero(), + Serialized::serialize(OnMinerSectorsTerminateParams { deal_ids })?, + TokenAmount::zero(), )?; Ok(()) } @@ -2090,10 +2085,10 @@ where })?; rt.send( - &*STORAGE_POWER_ACTOR_ADDR, + *STORAGE_POWER_ACTOR_ADDR, PowerMethod::OnSectorTerminate as u64, - &ser_params, - &TokenAmount::zero(), + ser_params, + TokenAmount::zero(), )?; Ok(()) } @@ -2206,13 +2201,13 @@ where RT: Runtime, { let ret = rt.send( - &*STORAGE_MARKET_ACTOR_ADDR, + *STORAGE_MARKET_ACTOR_ADDR, MarketMethod::ComputeDataCommitment as u64, - &Serialized::serialize(ComputeDataCommitmentParams { + Serialized::serialize(ComputeDataCommitmentParams { sector_type, deal_ids, })?, - &TokenAmount::zero(), + TokenAmount::zero(), )?; let unsealed_cid: Cid = ret.deserialize()?; Ok(unsealed_cid) @@ -2265,24 +2260,17 @@ where BS: BlockStore, RT: Runtime, { - let resolved = rt.resolve_address(&raw).map_err(|_| { - ActorError::new( - ExitCode::ErrIllegalArgument, - format!("unable to resolve address {}", raw), - ) - })?; + let resolved = rt + .resolve_address(&raw)? + .ok_or_else(|| actor_error!(ErrIllegalArgument; "unable to resolve address: {}", raw))?; assert!(resolved.protocol() == Protocol::ID); - let owner_code = rt.get_actor_code_cid(&resolved).map_err(|_| { - ActorError::new( - ExitCode::ErrIllegalArgument, - format!("no code for address: {}", resolved), - ) - })?; + let owner_code = rt + .get_actor_code_cid(&resolved)? + .ok_or_else(|| actor_error!(ErrIllegalArgument; "no code for address: {}", resolved))?; if !is_principal(&owner_code) { - return Err(ActorError::new( - ExitCode::ErrIllegalArgument, - format!("owner actor type must be a principal, was {}", owner_code), + return Err(actor_error!(ErrIllegalArgument; + "owner actor type must be a principal, was {}", owner_code )); } @@ -2296,48 +2284,34 @@ where BS: BlockStore, RT: Runtime, { - let resolved = rt.resolve_address(&raw).map_err(|e| { - ActorError::new( - ExitCode::ErrIllegalArgument, - format!("unable to resolve address: {},{}", raw, e), - ) - })?; + let resolved = rt + .resolve_address(&raw)? + .ok_or_else(|| actor_error!(ErrIllegalArgument; "unable to resolve address: {}", raw))?; assert!(resolved.protocol() == Protocol::ID); - let owner_code = rt.get_actor_code_cid(&resolved).map_err(|e| { - ActorError::new( - ExitCode::ErrIllegalArgument, - format!("no code for address: {}, {}", resolved, e), - ) - })?; + let owner_code = rt + .get_actor_code_cid(&resolved)? + .ok_or_else(|| actor_error!(ErrIllegalArgument; "no code for address: {}", resolved))?; if owner_code != *ACCOUNT_ACTOR_CODE_ID { - return Err(ActorError::new( - ExitCode::ErrIllegalArgument, - format!("worker actor type must be an account, was {}", owner_code), - )); + return Err(actor_error!(ErrIllegalArgument; + "worker actor type must be an account, was {}", owner_code)); } if raw.protocol() != Protocol::BLS { let ret = rt.send( - &resolved, + resolved, AccountMethod::PubkeyAddress as u64, - &Serialized::default(), - &TokenAmount::zero(), + Serialized::default(), + TokenAmount::zero(), )?; let pub_key: Address = ret.deserialize().map_err(|e| { - ActorError::new( - ExitCode::ErrSerialization, - format!("failed to deserialize address result: {:?}, {}", ret, e), - ) + actor_error!(ErrSerialization; "failed to deserialize address result: {:?}, {}", ret, e) })?; if pub_key.protocol() != Protocol::BLS { - return Err(ActorError::new( - ExitCode::ErrIllegalArgument, - format!( + return Err(actor_error!(ErrIllegalArgument; "worker account {} must have BLS pubkey, was {}", resolved, pub_key.protocol() - ), )); } } @@ -2346,26 +2320,26 @@ where fn burn_funds_and_notify_pledge_change( rt: &mut RT, - amount: &TokenAmount, + amount: TokenAmount, ) -> Result<(), ActorError> where BS: BlockStore, RT: Runtime, { - burn_funds(rt, amount)?; - notify_pledge_change(rt, &amount.clone().neg()) + burn_funds(rt, amount.clone())?; + notify_pledge_change(rt, &amount.neg()) } -fn burn_funds(rt: &mut RT, amount: &TokenAmount) -> Result<(), ActorError> +fn burn_funds(rt: &mut RT, amount: TokenAmount) -> Result<(), ActorError> where BS: BlockStore, RT: Runtime, { - if amount > &BigInt::zero() { + if amount > BigInt::zero() { rt.send( - &*BURNT_FUNDS_ACTOR_ADDR, + *BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, - &Serialized::default(), + Serialized::default(), amount, )?; } @@ -2378,10 +2352,10 @@ where { if !pledge_delta.is_zero() { rt.send( - &*STORAGE_POWER_ACTOR_ADDR, + *STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, - &Serialized::serialize(BigIntSer(pledge_delta))?, - &TokenAmount::zero(), + Serialized::serialize(BigIntSer(pledge_delta))?, + TokenAmount::zero(), )?; } Ok(()) diff --git a/vm/actor/src/builtin/multisig/mod.rs b/vm/actor/src/builtin/multisig/mod.rs index ce72f2c04ae1..88605af9312a 100644 --- a/vm/actor/src/builtin/multisig/mod.rs +++ b/vm/actor/src/builtin/multisig/mod.rs @@ -186,7 +186,8 @@ impl Actor { BS: BlockStore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(rt.message().receiver()))?; + let receiver = *rt.message().receiver(); + rt.validate_immediate_caller_is(std::iter::once(&receiver))?; rt.transaction::(|st, _| { // Check if signer to add is already signer @@ -213,7 +214,8 @@ impl Actor { BS: BlockStore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(rt.message().receiver()))?; + let receiver = *rt.message().receiver(); + rt.validate_immediate_caller_is(std::iter::once(&receiver))?; rt.transaction::(|st, _| { // Check that signer to remove exists @@ -248,7 +250,8 @@ impl Actor { BS: BlockStore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(rt.message().receiver()))?; + let receiver = *rt.message().receiver(); + rt.validate_immediate_caller_is(std::iter::once(&receiver))?; rt.transaction::(|st, _| { // Check that signer to remove exists @@ -286,7 +289,8 @@ impl Actor { BS: BlockStore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(rt.message().receiver()))?; + let receiver = *rt.message().receiver(); + rt.validate_immediate_caller_is(std::iter::once(&receiver))?; rt.transaction::(|st, _| { // Check if valid threshold value @@ -371,7 +375,7 @@ impl Actor { // Sufficient number of approvals have arrived, relay message if threshold_met { - rt.send(&tx.to, tx.method, &tx.params, &tx.value)?; + rt.send(tx.to, tx.method, tx.params, tx.value)?; } Ok(()) diff --git a/vm/actor/src/builtin/paych/mod.rs b/vm/actor/src/builtin/paych/mod.rs index 7d2d576a3efc..09244269e40d 100644 --- a/vm/actor/src/builtin/paych/mod.rs +++ b/vm/actor/src/builtin/paych/mod.rs @@ -15,7 +15,8 @@ use num_derive::FromPrimitive; use num_traits::{FromPrimitive, Zero}; use runtime::{ActorCode, Runtime}; use vm::{ - ActorError, ExitCode, MethodNum, Serialized, TokenAmount, METHOD_CONSTRUCTOR, METHOD_SEND, + actor_error, ActorError, ExitCode, MethodNum, Serialized, TokenAmount, METHOD_CONSTRUCTOR, + METHOD_SEND, }; /// Payment Channel actor methods available @@ -42,9 +43,11 @@ impl Actor { rt.validate_immediate_caller_type(std::iter::once(&*INIT_ACTOR_CODE_ID))?; // Check both parties are capable of signing vouchers - let to = Self::resolve_account(rt, ¶ms.to)?; + let to = Self::resolve_account(rt, ¶ms.to) + .map_err(|e| actor_error!(ErrIllegalArgument; e))?; - let from = Self::resolve_account(rt, ¶ms.from)?; + let from = Self::resolve_account(rt, ¶ms.from) + .map_err(|e| actor_error!(ErrIllegalArgument; e))?; rt.create(&State::new(from, to))?; Ok(()) @@ -53,22 +56,26 @@ impl Actor { /// Resolves an address to a canonical ID address and requires it to address an account actor. /// The account actor constructor checks that the embedded address is associated with an appropriate key. /// An alternative (more expensive) would be to send a message to the actor to fetch its key. - fn resolve_account(rt: &RT, raw: &Address) -> Result + fn resolve_account(rt: &RT, raw: &Address) -> Result where BS: BlockStore, RT: Runtime, { - let resolved = rt.resolve_address(raw)?; + let resolved = rt + // TODO: fatal error not handled here. To match go this will have to be refactored + .resolve_address(raw) + .map_err(|e| e.to_string())? + .ok_or_else(|| format!("failed to resolve address {}", raw))?; - let code_cid = rt.get_actor_code_cid(&resolved)?; + let code_cid = rt + .get_actor_code_cid(&resolved) + .expect("Failed to get code Cid") + .ok_or_else(|| format!("no code for address {}", raw))?; if code_cid != *ACCOUNT_ACTOR_CODE_ID { - Err(ActorError::new( - ExitCode::ErrIllegalArgument, - format!( - "actor {} must be an account ({}), was {}", - raw, &*ACCOUNT_ACTOR_CODE_ID, code_cid - ), + Err(format!( + "actor {} must be an account ({}), was {}", + raw, &*ACCOUNT_ACTOR_CODE_ID, code_cid )) } else { Ok(resolved) @@ -140,13 +147,13 @@ impl Actor { if let Some(extra) = &sv.extra { rt.send( - &extra.actor, + extra.actor, extra.method, - &Serialized::serialize(PaymentVerifyParams { + Serialized::serialize(PaymentVerifyParams { extra: extra.data.clone(), proof: params.proof, })?, - &TokenAmount::from(0u8), + TokenAmount::from(0u8), )?; } @@ -300,10 +307,10 @@ impl Actor { })?; // send remaining balance to `from` - rt.send(&st.from, METHOD_SEND, &Serialized::default(), &rem_bal)?; + rt.send(st.from, METHOD_SEND, Serialized::default(), rem_bal)?; // send ToSend to `to` - rt.send(&st.to, METHOD_SEND, &Serialized::default(), &st.to_send)?; + rt.send(st.to, METHOD_SEND, Serialized::default(), st.to_send)?; rt.transaction(|st: &mut State, _| { st.to_send = TokenAmount::from(0u8); diff --git a/vm/actor/src/builtin/power/mod.rs b/vm/actor/src/builtin/power/mod.rs index 1033c1942a5f..d444c9bf5720 100644 --- a/vm/actor/src/builtin/power/mod.rs +++ b/vm/actor/src/builtin/power/mod.rs @@ -83,7 +83,12 @@ impl Actor { let value = rt.message().value_received().clone(); // TODO update this send, is now outdated let addresses: init::ExecReturn = rt - .send(&INIT_ACTOR_ADDR, init::Method::Exec as u64, params, &value)? + .send( + *INIT_ACTOR_ADDR, + init::Method::Exec as u64, + params.clone(), + value, + )? .deserialize()?; rt.transaction::, _>(|st, rt| { @@ -110,11 +115,12 @@ impl Actor { BS: BlockStore, RT: Runtime, { - let nominal = rt.resolve_address(¶ms.miner)?; + // TODO this function does not exist anymore, make sure it is removed/replaced later + let nominal = rt.resolve_address(¶ms.miner)?.unwrap(); let st: State = rt.state()?; - let (owner_addr, worker_addr) = request_miner_control_addrs(rt, &nominal)?; + let (owner_addr, worker_addr) = request_miner_control_addrs(rt, nominal)?; rt.validate_immediate_caller_is(&[owner_addr, worker_addr])?; let claim = st @@ -357,10 +363,10 @@ impl Actor { for event in cron_events { // TODO switch 12 to OnDeferredCronEvent on miner actor impl rt.send( - &event.miner_addr, + event.miner_addr, 12, - &event.callback_payload, - &TokenAmount::from(0u8), + event.callback_payload, + TokenAmount::from(0u8), )?; } @@ -490,10 +496,10 @@ where { let st: State = rt.state()?; let ret = rt.send( - &*REWARD_ACTOR_ADDR, + *REWARD_ACTOR_ADDR, RewardMethod::LastPerEpochReward as u64, - &Serialized::default(), - &TokenAmount::zero(), + Serialized::default(), + TokenAmount::zero(), )?; let BigIntDe(epoch_reward) = ret.deserialize()?; diff --git a/vm/actor/src/builtin/reward/mod.rs b/vm/actor/src/builtin/reward/mod.rs index 8d5d5d15d990..a95843e2dd69 100644 --- a/vm/actor/src/builtin/reward/mod.rs +++ b/vm/actor/src/builtin/reward/mod.rs @@ -20,7 +20,8 @@ use num_derive::FromPrimitive; use num_traits::FromPrimitive; use runtime::{ActorCode, Runtime}; use vm::{ - ActorError, ExitCode, MethodNum, Serialized, TokenAmount, METHOD_CONSTRUCTOR, METHOD_SEND, + actor_error, ActorError, ExitCode, MethodNum, Serialized, TokenAmount, METHOD_CONSTRUCTOR, + METHOD_SEND, }; // * Updated to specs-actors commit: 52599b21919df07f44d7e61cc028e265ec18f700 @@ -84,10 +85,9 @@ impl Actor { "cannot give block reward for zero tickets" ); - let miner_addr = rt - .resolve_address(¶ms.miner) - // TODO revisit later if all address resolutions end up being the same exit code - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.msg().to_string()))?; + let miner_addr = rt.resolve_address(¶ms.miner)?.ok_or_else( + || actor_error!(ErrIllegalState; "failed to resolve given owner address"), + )?; let prior_balance = rt.current_balance()?; @@ -107,29 +107,29 @@ impl Actor { ); rt.send( - &miner_addr, + miner_addr, miner::Method::AddLockedFund as u64, - &Serialized::serialize(&BigIntSer(&reward_payable))?, - &reward_payable, + Serialized::serialize(&BigIntSer(&reward_payable))?, + reward_payable, )?; // Burn the penalty rt.send( - &*BURNT_FUNDS_ACTOR_ADDR, + *BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, - &Serialized::default(), - &penalty, + Serialized::default(), + penalty.clone(), )?; Ok(()) } - fn last_per_epoch_reward(rt: &RT) -> Result + fn last_per_epoch_reward(rt: &mut RT) -> Result where BS: BlockStore, RT: Runtime, { - rt.validate_immediate_caller_accept_any(); + rt.validate_immediate_caller_accept_any()?; let st: State = rt.state()?; Ok(st.last_per_epoch_reward) } diff --git a/vm/actor/src/builtin/shared.rs b/vm/actor/src/builtin/shared.rs index a7e461e18cf6..7b130d0080f3 100644 --- a/vm/actor/src/builtin/shared.rs +++ b/vm/actor/src/builtin/shared.rs @@ -11,7 +11,7 @@ use vm::{ActorError, Serialized, TokenAmount}; pub(crate) fn request_miner_control_addrs( rt: &mut RT, - miner_addr: &Address, + miner_addr: Address, ) -> Result<(Address, Address), ActorError> where BS: BlockStore, @@ -20,8 +20,8 @@ where let ret = rt.send( miner_addr, Method::ControlAddresses as u64, - &Serialized::default(), - &TokenAmount::zero(), + Serialized::default(), + TokenAmount::zero(), )?; let addrs: MinerAddrs = ret.deserialize()?; diff --git a/vm/actor/src/builtin/system/mod.rs b/vm/actor/src/builtin/system/mod.rs index 278ea320b056..e34c7159b26b 100644 --- a/vm/actor/src/builtin/system/mod.rs +++ b/vm/actor/src/builtin/system/mod.rs @@ -20,7 +20,7 @@ pub enum Method { pub struct Actor; impl Actor { /// Init actor constructor - pub fn constructor(rt: &RT) -> Result<(), ActorError> + pub fn constructor(rt: &mut RT) -> Result<(), ActorError> where BS: BlockStore, RT: Runtime, diff --git a/vm/actor/src/builtin/verifreg/mod.rs b/vm/actor/src/builtin/verifreg/mod.rs index 30b4aad22f3b..2f60b4fe9d58 100644 --- a/vm/actor/src/builtin/verifreg/mod.rs +++ b/vm/actor/src/builtin/verifreg/mod.rs @@ -112,7 +112,7 @@ impl Actor { )); } - rt.validate_immediate_caller_accept_any(); + rt.validate_immediate_caller_accept_any()?; rt.transaction(|st: &mut State, rt| { // Validate caller is one of the verifiers. let verify_addr = rt.message().caller(); diff --git a/vm/actor/tests/account_actor_test.rs b/vm/actor/tests/account_actor_test.rs index 200d05cc738f..d49dddfec79e 100644 --- a/vm/actor/tests/account_actor_test.rs +++ b/vm/actor/tests/account_actor_test.rs @@ -21,7 +21,7 @@ macro_rules! account_tests { caller_type: SYSTEM_ACTOR_CODE_ID.clone(), ..Default::default() }; - rt.expect_validate_caller_addr(&[*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); if exit_code.is_success() { rt diff --git a/vm/actor/tests/common/mod.rs b/vm/actor/tests/common/mod.rs index b4da851dcaa9..9608aa552f06 100644 --- a/vm/actor/tests/common/mod.rs +++ b/vm/actor/tests/common/mod.rs @@ -4,7 +4,7 @@ use actor::{ self, ACCOUNT_ACTOR_CODE_ID, CRON_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID, MARKET_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID, POWER_ACTOR_CODE_ID, - REWARD_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, VERIFIED_ACTOR_CODE_ID, + REWARD_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, VERIFREG_ACTOR_CODE_ID, }; use address::Address; use cid::{multihash::Blake2b256, Cid}; @@ -18,7 +18,7 @@ use runtime::{ActorCode, ConsensusFault, MessageInfo, Runtime, Syscalls}; use std::cell::{Cell, RefCell}; use std::collections::{HashMap, VecDeque}; use std::error::Error as StdError; -use vm::{ActorError, ExitCode, MethodNum, Randomness, Serialized, TokenAmount}; +use vm::{actor_error, ActorError, ExitCode, MethodNum, Randomness, Serialized, TokenAmount}; pub struct MockRuntime { pub epoch: ChainEpoch, @@ -45,8 +45,8 @@ pub struct MockRuntime { // Expectations pub expect_validate_caller_any: Cell, - pub expect_validate_caller_addr: RefCell>>, - pub expect_validate_caller_type: RefCell>>, + pub expect_validate_caller_addr: Option>, + pub expect_validate_caller_type: Option>, pub expect_sends: VecDeque, pub expect_create_actor: Option, pub expect_verify_sigs: RefCell>, @@ -152,7 +152,7 @@ impl MockRuntime { } fn check_argument(&self, predicate: bool, msg: String) -> Result<(), ActorError> { if !predicate { - return Err(ActorError::new(ExitCode::SysErrorIllegalArgument, msg)); + return Err(actor_error!(SysErrorIllegalArgument; msg)); } Ok(()) } @@ -172,9 +172,9 @@ impl MockRuntime { .unwrap(); Ok(data) } - pub fn expect_validate_caller_addr(&self, addr: &[Address]) { + pub fn expect_validate_caller_addr(&mut self, addr: Vec
) { assert!(addr.len() > 0, "addrs must be non-empty"); - *self.expect_validate_caller_addr.borrow_mut() = Some(addr.to_vec()); + self.expect_validate_caller_addr = Some(addr); } #[allow(dead_code)] @@ -207,9 +207,9 @@ impl MockRuntime { } #[allow(dead_code)] - pub fn expect_validate_caller_type(&self, types: &[Cid]) { + pub fn expect_validate_caller_type(&mut self, types: Vec) { assert!(types.len() > 0, "addrs must be non-empty"); - *self.expect_validate_caller_type.borrow_mut() = Some(types.to_vec()); + self.expect_validate_caller_type = Some(types); } #[allow(dead_code)] @@ -257,13 +257,10 @@ impl MockRuntime { x if x == &*REWARD_ACTOR_CODE_ID => { actor::reward::Actor.invoke_method(self, method_num, params) } - x if x == &*VERIFIED_ACTOR_CODE_ID => { + x if x == &*VERIFREG_ACTOR_CODE_ID => { actor::verifreg::Actor.invoke_method(self, method_num, params) } - _ => Err(ActorError::new( - ExitCode::SysErrForbidden, - "invalid method id".to_owned(), - )), + _ => Err(actor_error!(SysErrForbidden; "invalid method id")), }; if res.is_err() { @@ -278,14 +275,14 @@ impl MockRuntime { "expected ValidateCallerAny, not received" ); assert!( - self.expect_validate_caller_addr.borrow().as_ref().is_none(), + self.expect_validate_caller_addr.is_none(), "expected ValidateCallerAddr {:?}, not received", - self.expect_validate_caller_addr.borrow().as_ref().unwrap() + self.expect_validate_caller_addr ); assert!( - self.expect_validate_caller_type.borrow().as_ref().is_none(), + self.expect_validate_caller_type.is_none(), "expected ValidateCallerType {:?}, not received", - self.expect_validate_caller_type.borrow().as_ref().unwrap() + self.expect_validate_caller_type ); assert!( self.expect_sends.is_empty(), @@ -314,15 +311,15 @@ impl MockRuntime { .borrow() .as_ref() .is_none(), - "expect_compute_unsealed_sector_cid not received", + "expect_verify_consensus_fault not received", ); self.reset(); } pub fn reset(&mut self) { self.expect_validate_caller_any.set(false); - *self.expect_validate_caller_addr.borrow_mut() = None; - *self.expect_validate_caller_type.borrow_mut() = None; + self.expect_validate_caller_addr = None; + self.expect_validate_caller_type = None; self.expect_create_actor = None; self.expect_verify_sigs.borrow_mut().clear(); *self.expect_verify_seal.borrow_mut() = None; @@ -405,16 +402,17 @@ impl Runtime for MockRuntime { self.epoch } - fn validate_immediate_caller_accept_any(&self) { + fn validate_immediate_caller_accept_any(&mut self) -> Result<(), ActorError> { self.require_in_call(); assert!( self.expect_validate_caller_any.get(), "unexpected validate-caller-any" ); self.expect_validate_caller_any.set(false); + Ok(()) } - fn validate_immediate_caller_is<'a, I>(&self, addresses: I) -> Result<(), ActorError> + fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), ActorError> where I: IntoIterator, { @@ -425,33 +423,29 @@ impl Runtime for MockRuntime { self.check_argument(addrs.len() > 0, "addrs must be non-empty".to_owned())?; assert!( - self.expect_validate_caller_addr.borrow().is_some(), + self.expect_validate_caller_addr.is_some(), "unexpected validate caller addrs" ); assert!( - &addrs == self.expect_validate_caller_addr.borrow().as_ref().unwrap(), + &addrs == self.expect_validate_caller_addr.as_ref().unwrap(), "unexpected validate caller addrs {:?}, expected {:?}", addrs, - self.expect_validate_caller_addr.borrow().as_ref() + self.expect_validate_caller_addr ); for expected in &addrs { if self.message().caller() == expected { - *self.expect_validate_caller_addr.borrow_mut() = None; + self.expect_validate_caller_addr = None; return Ok(()); } } - *self.expect_validate_caller_addr.borrow_mut() = None; - return Err(ActorError::new( - ExitCode::ErrForbidden, - format!( + self.expect_validate_caller_addr = None; + return Err(actor_error!(ErrForbidden; "caller address {:?} forbidden, allowed: {:?}", - self.message().caller(), - &addrs - ), + self.message().caller(), &addrs )); } - fn validate_immediate_caller_type<'a, I>(&self, types: I) -> Result<(), ActorError> + fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError> where I: IntoIterator, { @@ -461,11 +455,11 @@ impl Runtime for MockRuntime { self.check_argument(types.len() > 0, "types must be non-empty".to_owned())?; assert!( - self.expect_validate_caller_type.borrow().is_some(), + self.expect_validate_caller_type.is_some(), "unexpected validate caller code" ); assert!( - &types == self.expect_validate_caller_type.borrow().as_ref().unwrap(), + &types == self.expect_validate_caller_type.as_ref().unwrap(), "unexpected validate caller code {:?}, expected {:?}", types, self.expect_validate_caller_type @@ -473,20 +467,17 @@ impl Runtime for MockRuntime { for expected in &types { if &self.caller_type == expected { - *self.expect_validate_caller_type.borrow_mut() = None; + self.expect_validate_caller_type = None; return Ok(()); } } - *self.expect_validate_caller_type.borrow_mut() = None; + self.expect_validate_caller_type = None; - Err(self.abort( - ExitCode::ErrForbidden, - format!( - "caller type {:?} forbidden, allowed: {:?}", - self.caller_type, types - ), - )) + Err( + actor_error!(ErrForbidden; "caller type {:?} forbidden, allowed: {:?}", + self.caller_type, types), + ) } fn current_balance(&self) -> Result { @@ -494,31 +485,19 @@ impl Runtime for MockRuntime { Ok(self.balance.clone()) } - fn resolve_address(&self, address: &Address) -> Result { + fn resolve_address(&self, address: &Address) -> Result, ActorError> { self.require_in_call(); if address.protocol() == address::Protocol::ID { - return Ok(address.clone()); + return Ok(Some(address.clone())); } - self.id_addresses - .get(&address) - .cloned() - .ok_or(ActorError::new( - ExitCode::ErrIllegalArgument, - "Address not found".to_string(), - )) + Ok(self.id_addresses.get(&address).cloned()) } - fn get_actor_code_cid(&self, addr: &Address) -> Result { + fn get_actor_code_cid(&self, addr: &Address) -> Result, ActorError> { self.require_in_call(); - self.actor_code_cids - .get(&addr) - .cloned() - .ok_or(ActorError::new( - ExitCode::ErrIllegalArgument, - "Actor address is not found".to_string(), - )) + Ok(self.actor_code_cids.get(&addr).cloned()) } fn get_randomness( @@ -532,10 +511,7 @@ impl Runtime for MockRuntime { fn create(&mut self, obj: &C) -> Result<(), ActorError> { if self.state.is_some() == true { - return Err(self.abort( - ExitCode::SysErrorIllegalActor, - "state already constructed".to_owned(), - )); + return Err(actor_error!(SysErrorIllegalActor; "state already constructed")); } self.state = Some(self.store.put(obj, Blake2b256).unwrap()); Ok(()) @@ -570,17 +546,14 @@ impl Runtime for MockRuntime { fn send( &mut self, - to: &Address, + to: Address, method: MethodNum, - params: &Serialized, - value: &TokenAmount, + params: Serialized, + value: TokenAmount, ) -> Result { self.require_in_call(); if self.in_transaction { - return Err(self.abort( - ExitCode::SysErrorIllegalActor, - "side-effect within transaction", - )); + return Err(actor_error!(SysErrorIllegalActor; "side-effect within transaction")); } assert!( @@ -594,15 +567,12 @@ impl Runtime for MockRuntime { let expected_msg = self.expect_sends.pop_front().unwrap(); - assert!(&expected_msg.to == to && expected_msg.method == method && &expected_msg.params == params && &expected_msg.value == value, "expectedMessage being sent does not match expectation.\nMessage -\t to: {:?} method: {:?} value: {:?} params: {:?}\nExpected -\t {:?}", to, method, value, params, self.expect_sends[0]); + assert!(expected_msg.to == to && expected_msg.method == method && expected_msg.params == params && expected_msg.value == value, "expectedMessage being sent does not match expectation.\nMessage -\t to: {:?} method: {:?} value: {:?} params: {:?}\nExpected -\t {:?}", to, method, value, params, self.expect_sends[0]); - if value > &self.balance { - return Err(self.abort( - ExitCode::SysErrSenderStateInvalid, - format!( + if value > self.balance { + return Err(actor_error!(SysErrSenderStateInvalid; "cannot send value: {:?} exceeds balance: {:?}", value, self.balance - ), )); } self.balance -= value; @@ -633,10 +603,7 @@ impl Runtime for MockRuntime { fn create_actor(&mut self, code_id: &Cid, address: &Address) -> Result<(), ActorError> { self.require_in_call(); if self.in_transaction { - return Err(self.abort( - ExitCode::SysErrorIllegalActor, - "side-effect within transaction".to_owned(), - )); + return Err(actor_error!(SysErrorIllegalActor; "side-effect within transaction")); } let expect_create_actor = self .expect_create_actor @@ -650,10 +617,7 @@ impl Runtime for MockRuntime { fn delete_actor(&mut self, _beneficiary: &Address) -> Result<(), ActorError> { self.require_in_call(); if self.in_transaction { - return Err(self.abort( - ExitCode::SysErrorIllegalActor, - "side-effect within transaction".to_owned(), - )); + return Err(actor_error!(SysErrorIllegalActor; "side-effect within transaction")); } todo!("implement me???") } @@ -675,19 +639,15 @@ impl Syscalls for MockRuntime { plaintext: &[u8], ) -> Result<(), Box> { if self.expect_verify_sigs.borrow().len() == 0 { - return Err(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Unexpected signature verification".to_string(), - ))); + return Err(Box::new( + actor_error!(ErrIllegalState; "Unexpected signature verification"), + )); } let exp = self .expect_verify_sigs .borrow_mut() .pop() - .ok_or(ActorError::new( - ExitCode::ErrIllegalState, - "Unexpected signature verification".to_string(), - ))?; + .ok_or(actor_error!(ErrIllegalState; "Unexpected signature verification"))?; if exp.sig == *signature && exp.signer == *signer && &exp.plaintext[..] == plaintext { if exp.result == ExitCode::Ok { return Ok(()); @@ -698,10 +658,9 @@ impl Syscalls for MockRuntime { ))); } } else { - return Err(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Signatures did not match".to_string(), - ))); + return Err(Box::new( + actor_error!(ErrIllegalState; "Signatures did not match"), + )); } } @@ -716,22 +675,19 @@ impl Syscalls for MockRuntime { let exp = self .expect_compute_unsealed_sector_cid .replace(None) - .ok_or(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Unexpected syscall to ComputeUnsealedSectorCID".to_string(), + .ok_or(Box::new(actor_error!(ErrIllegalState; + "Unexpected syscall to ComputeUnsealedSectorCID" )))?; if exp.reg != reg { - return Err(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Unexpected compute_unsealed_sector_cid : reg mismatch".to_string(), + return Err(Box::new(actor_error!(ErrIllegalState; + "Unexpected compute_unsealed_sector_cid : reg mismatch" ))); } if exp.pieces[..].eq(pieces) { - return Err(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Unexpected compute_unsealed_sector_cid : pieces mismatch".to_string(), + return Err(Box::new(actor_error!(ErrIllegalState; + "Unexpected compute_unsealed_sector_cid : pieces mismatch" ))); } @@ -744,19 +700,14 @@ impl Syscalls for MockRuntime { Ok(exp.cid) } fn verify_seal(&self, seal: &SealVerifyInfo) -> Result<(), Box> { - let exp = self - .expect_verify_seal - .replace(None) - .ok_or(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Unexpected syscall to verify seal".to_string(), - )))?; + let exp = self.expect_verify_seal.replace(None).ok_or(Box::new( + actor_error!(ErrIllegalState; "Unexpected syscall to verify seal"), + ))?; if exp.seal != *seal { - return Err(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Unexpected seal verification".to_string(), - ))); + return Err(Box::new( + actor_error!(ErrIllegalState; "Unexpected seal verification"), + )); } if exp.exit_code != ExitCode::Ok { return Err(Box::new(ActorError::new( @@ -767,19 +718,14 @@ impl Syscalls for MockRuntime { Ok(()) } fn verify_post(&self, post: &WindowPoStVerifyInfo) -> Result<(), Box> { - let exp = self - .expect_verify_post - .replace(None) - .ok_or(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Unexpected syscall to verify PoSt ".to_string(), - )))?; + let exp = self.expect_verify_post.replace(None).ok_or(Box::new( + actor_error!(ErrIllegalState; "Unexpected syscall to verify PoSt"), + ))?; if exp.post != *post { - return Err(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Unexpected PoSt verification".to_string(), - ))); + return Err(Box::new( + actor_error!(ErrIllegalState; "Unexpected PoSt verification"), + )); } if exp.exit_code != ExitCode::Ok { return Err(Box::new(ActorError::new( @@ -798,28 +744,20 @@ impl Syscalls for MockRuntime { let exp = self .expect_verify_consensus_fault .replace(None) - .ok_or(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Unexpected syscall to verify_consensus_fault".to_string(), - )))?; + .ok_or(Box::new( + actor_error!(ErrIllegalState; "Unexpected syscall to verify_consensus_fault"), + ))?; if exp.require_correct_input { if exp.block_header_1 != h1 { - return Err(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Header 1 mismatch".to_string(), - ))); + return Err(Box::new(actor_error!(ErrIllegalState; "Header 1 mismatch"))); } if exp.block_header_2 != h2 { - return Err(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Header 2 mismatch".to_string(), - ))); + return Err(Box::new(actor_error!(ErrIllegalState; "Header 2 mismatch"))); } if exp.block_header_extra != extra { - return Err(Box::new(ActorError::new( - ExitCode::ErrIllegalState, - "Header extra mismatch".to_string(), - ))); + return Err(Box::new( + actor_error!(ErrIllegalState; "Header extra mismatch"), + )); } } if exp.exit_code != ExitCode::Ok { diff --git a/vm/actor/tests/cron_actor_test.rs b/vm/actor/tests/cron_actor_test.rs index 460c94e2ea76..7989954775ba 100644 --- a/vm/actor/tests/cron_actor_test.rs +++ b/vm/actor/tests/cron_actor_test.rs @@ -137,7 +137,7 @@ fn epoch_tick_with_entries() { } fn construct_and_verify(rt: &mut MockRuntime, params: &ConstructorParams) { - rt.expect_validate_caller_addr(&[*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); let ret = rt .call( &*CRON_ACTOR_CODE_ID, @@ -150,7 +150,7 @@ fn construct_and_verify(rt: &mut MockRuntime, params: &ConstructorParams) { } fn epoch_tick_and_verify(rt: &mut MockRuntime) { - rt.expect_validate_caller_addr(&[*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); let ret = rt .call(&*CRON_ACTOR_CODE_ID, 2, &Serialized::default()) .unwrap(); diff --git a/vm/actor/tests/init_actor_test.rs b/vm/actor/tests/init_actor_test.rs index 5fd31f2c4600..c7a0843bbe1c 100644 --- a/vm/actor/tests/init_actor_test.rs +++ b/vm/actor/tests/init_actor_test.rs @@ -89,7 +89,8 @@ fn create_2_payment_channels() { let state: State = rt.get_state().unwrap(); let returned_address = state .resolve_address(&rt.store, &unique_address) - .expect("Address should have been found"); + .expect("Resolve should not error") + .expect("Address should be able to be resolved"); assert_eq!(returned_address, expected_id_addr, "Wrong Address returned"); } @@ -135,14 +136,18 @@ fn create_storage_miner() { let state: State = rt.get_state().unwrap(); let returned_address = state .resolve_address(&rt.store, &unique_address) - .expect("Address should have been found"); + .expect("Resolve should not error") + .expect("Address should be able to be resolved"); assert_eq!(expected_id_addr, returned_address); // Should return error since the address of flurbo is unknown let unknown_addr = Address::new_actor(b"flurbo"); - state - .resolve_address(&rt.store, &unknown_addr) - .expect_err("Address should have not been found"); + + let returned_address = state.resolve_address(&rt.store, &unknown_addr).unwrap(); + assert_eq!( + returned_address, None, + "Addresses should have not been found" + ); } #[test] @@ -232,18 +237,15 @@ fn sending_constructor_failure() { let state: State = rt.get_state().unwrap(); - let returned_address = state - .resolve_address(&rt.store, &unique_address) - .expect_err("Address resolution should have failed"); - + let returned_address = state.resolve_address(&rt.store, &unique_address).unwrap(); assert_eq!( - returned_address, "Address not found", + returned_address, None, "Addresses should have not been found" ); } fn construct_and_verify(rt: &mut MockRuntime) { - rt.expect_validate_caller_addr(&[SYSTEM_ACTOR_ADDR.clone()]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR.clone()]); let params = ConstructorParams { network_name: "mock".to_string(), }; diff --git a/vm/actor/tests/market_actor_test.rs b/vm/actor/tests/market_actor_test.rs index 41d943217efc..804e78e39b9e 100644 --- a/vm/actor/tests/market_actor_test.rs +++ b/vm/actor/tests/market_actor_test.rs @@ -51,7 +51,7 @@ fn simple_construction() { ..Default::default() }; - rt.expect_validate_caller_addr(&[SYSTEM_ACTOR_ADDR.clone()]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR.clone()]); assert_eq!( Serialized::default(), @@ -164,9 +164,8 @@ fn add_non_provider_funds() { rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), caller_addr); let amount = TokenAmount::from(test_case.0 as u64); - // rt.balance = rt.balance + amount.clone(); rt.set_value(amount); - rt.expect_validate_caller_type(&CALLER_TYPES_SIGNABLE.clone()); + rt.expect_validate_caller_type(CALLER_TYPES_SIGNABLE.to_vec()); assert!(rt .call( @@ -272,7 +271,7 @@ fn withdraw_non_provider() { ); rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), client_addr.clone()); - rt.expect_validate_caller_type(&[ + rt.expect_validate_caller_type(vec![ ACCOUNT_ACTOR_CODE_ID.clone(), MULTISIG_ACTOR_CODE_ID.clone(), ]); @@ -323,7 +322,7 @@ fn client_withdraw_more_than_available() { add_participant_funds(&mut rt, client_addr.clone(), amount.clone()); rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), client_addr.clone()); - rt.expect_validate_caller_type(&[ + rt.expect_validate_caller_type(vec![ ACCOUNT_ACTOR_CODE_ID.clone(), MULTISIG_ACTOR_CODE_ID.clone(), ]); @@ -434,7 +433,7 @@ fn expect_provider_control_address( owner: Address, worker: Address, ) { - rt.expect_validate_caller_addr(&[owner.clone(), worker.clone()]); + rt.expect_validate_caller_addr(vec![owner.clone(), worker.clone()]); let return_value = GetControlAddressesReturn { owner: owner.clone(), @@ -481,7 +480,7 @@ fn add_participant_funds(rt: &mut MockRuntime, addr: Address, amount: TokenAmoun rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), addr.clone()); - rt.expect_validate_caller_type(&[ + rt.expect_validate_caller_type(vec![ ACCOUNT_ACTOR_CODE_ID.clone(), MULTISIG_ACTOR_CODE_ID.clone(), ]); @@ -500,7 +499,7 @@ fn add_participant_funds(rt: &mut MockRuntime, addr: Address, amount: TokenAmoun } fn construct_and_verify(rt: &mut MockRuntime) { - rt.expect_validate_caller_addr(&[SYSTEM_ACTOR_ADDR.clone()]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR.clone()]); assert_eq!( Serialized::default(), rt.call( diff --git a/vm/actor/tests/paych_actor_test.rs b/vm/actor/tests/paych_actor_test.rs index ac10cdb7eeef..37000784a15b 100644 --- a/vm/actor/tests/paych_actor_test.rs +++ b/vm/actor/tests/paych_actor_test.rs @@ -80,7 +80,7 @@ mod paych_constructor { #[test] fn actor_doesnt_exist_test() { let mut rt = construct_runtime(); - rt.expect_validate_caller_type(&[INIT_ACTOR_CODE_ID.clone()]); + rt.expect_validate_caller_type(vec![INIT_ACTOR_CODE_ID.clone()]); let params = ConstructorParams { to: Address::new_id(TEST_PAYCH_ADDR), from: Address::new_id(TEST_PAYER_ADDR), @@ -137,7 +137,7 @@ mod paych_constructor { ..Default::default() }; - rt.expect_validate_caller_type(&[INIT_ACTOR_CODE_ID.clone()]); + rt.expect_validate_caller_type(vec![INIT_ACTOR_CODE_ID.clone()]); let params = ConstructorParams { to: test_case.paych_addr, from: Address::new_id(10001), @@ -290,7 +290,7 @@ mod create_lane_tests { let ucp = UpdateChannelStateParams::from(sv.clone()); rt.set_caller(test_case.target_code, payee_addr); - rt.expect_validate_caller_addr(&[payer_addr, payee_addr]); + rt.expect_validate_caller_addr(vec![payer_addr, payee_addr]); if test_case.sig.is_some() && test_case.secret_preimage.len() == 0 { let exp_exit_code = if !test_case.verify_sig { @@ -343,7 +343,7 @@ mod update_channel_state_redeem { let payee_addr = Address::new_id(R_PAYEE_ADDR); rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), payee_addr); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); sv.amount = BigInt::from(9); @@ -386,7 +386,7 @@ mod update_channel_state_redeem { let payee_addr = Address::new_id(R_PAYEE_ADDR); rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), payee_addr); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); let initial_amount = state.to_send; sv.amount = BigInt::from(9); @@ -428,7 +428,7 @@ mod merge_tests { let (mut rt, sv) = require_create_cannel_with_lanes(num_lanes); let state: PState = rt.get_state().unwrap(); rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), state.from.clone()); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); (rt, sv, state) } @@ -600,7 +600,7 @@ mod update_channel_state_extra { let other_addr = Address::new_id(OTHER_ADDR); let fake_params = [1, 2, 3, 4]; rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), state.from); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); sv.extra = Some(ModVerifyParams { actor: other_addr, @@ -659,7 +659,7 @@ mod update_channel_state_settling { let (mut rt, sv) = require_create_cannel_with_lanes(1); rt.epoch = 10; let state: PState = rt.get_state().unwrap(); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), state.from); is_ok(&mut rt, Method::Settle as u64, &Serialized::default()); @@ -693,7 +693,7 @@ mod update_channel_state_settling { for tc in test_cases { let mut ucp = UpdateChannelStateParams::from(sv.clone()); ucp.sv.min_settle_height = tc.min_settle; - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); rt.expect_verify_signature(ExpectedVerifySig { sig: sv.clone().signature.unwrap(), signer: state.to, @@ -717,7 +717,7 @@ mod secret_preimage { fn succeed_correct_secret() { let (mut rt, sv) = require_create_cannel_with_lanes(1); let state: PState = rt.get_state().unwrap(); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); let ucp = UpdateChannelStateParams::from(sv.clone()); @@ -742,7 +742,7 @@ mod secret_preimage { let (mut rt, sv) = require_create_cannel_with_lanes(1); let state: PState = rt.get_state().unwrap(); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); let mut ucp = UpdateChannelStateParams { proof: vec![], @@ -779,7 +779,7 @@ mod actor_settle { rt.epoch = EP; let mut state: PState = rt.get_state().unwrap(); rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), state.from); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); is_ok(&mut rt, Method::Settle as u64, &Serialized::default()); @@ -795,10 +795,10 @@ mod actor_settle { rt.epoch = EP; let state: PState = rt.get_state().unwrap(); rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), state.from); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); is_ok(&mut rt, Method::Settle as u64, &Serialized::default()); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); expect_error( &mut rt, Method::Settle as u64, @@ -816,7 +816,7 @@ mod actor_settle { sv.min_settle_height = (EP + SETTLE_DELAY) + 1; let ucp = UpdateChannelStateParams::from(sv.clone()); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); rt.expect_verify_signature(ExpectedVerifySig { sig: ucp.sv.clone().signature.unwrap(), signer: state.to, @@ -832,7 +832,7 @@ mod actor_settle { assert_eq!(state.settling_at, 0); assert_eq!(state.min_settle_height, ucp.sv.min_settle_height); rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), state.from); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); is_ok(&mut rt, Method::Settle as u64, &Serialized::default()); state = rt.get_state().unwrap(); assert_eq!(state.settling_at, ucp.sv.min_settle_height); @@ -861,7 +861,7 @@ mod actor_collect { Serialized::default(), exit_codes[1], ); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); } #[test] @@ -870,12 +870,12 @@ mod actor_collect { rt.epoch = 10; let mut state: PState = rt.get_state().unwrap(); rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), state.from); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); is_ok(&mut rt, Method::Settle as u64, &Serialized::default()); state = rt.get_state().unwrap(); assert_eq!(state.settling_at, 11); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); exp_send_multiple(&mut rt, &state, [ExitCode::Ok, ExitCode::Ok]); @@ -923,7 +923,7 @@ mod actor_collect { if !tc.dont_settle { rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), state.from); - rt.expect_validate_caller_addr(&[state.from, state.to]); + rt.expect_validate_caller_addr(vec![state.from, state.to]); is_ok(&mut rt, Method::Settle as u64, &Serialized::default()); state = rt.get_state().unwrap(); assert_eq!(state.settling_at, 11); @@ -998,7 +998,7 @@ fn require_add_new_lane(rt: &mut MockRuntime, param: LaneParams) -> SignedVouche ..SignedVoucher::default() }; rt.set_caller(ACCOUNT_ACTOR_CODE_ID.clone(), param.from); - rt.expect_validate_caller_addr(&[param.from, param.to]); + rt.expect_validate_caller_addr(vec![param.from, param.to]); rt.expect_verify_signature(ExpectedVerifySig { sig: sig.clone(), signer: payee_addr, @@ -1027,7 +1027,7 @@ fn construct_and_verify(rt: &mut MockRuntime, sender: Address, receiver: Address from: sender, to: receiver, }; - rt.expect_validate_caller_type(&[INIT_ACTOR_CODE_ID.clone()]); + rt.expect_validate_caller_type(vec![INIT_ACTOR_CODE_ID.clone()]); is_ok( rt, METHOD_CONSTRUCTOR, diff --git a/vm/actor/tests/reward_actor_test.rs b/vm/actor/tests/reward_actor_test.rs index 80d63c5ef6f4..aebe77ea9f5b 100644 --- a/vm/actor/tests/reward_actor_test.rs +++ b/vm/actor/tests/reward_actor_test.rs @@ -29,7 +29,7 @@ fn balance_less_than_reward() { let miner = Address::new_id(1000); let gas_reward = TokenAmount::from(10u8); - rt.expect_validate_caller_addr(&[*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); let params = AwardBlockRewardParams { miner: miner, @@ -49,7 +49,7 @@ fn balance_less_than_reward() { } fn construct_and_verify(rt: &mut MockRuntime) { - rt.expect_validate_caller_addr(&[SYSTEM_ACTOR_ADDR.clone()]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR.clone()]); let ret = rt .call( &*REWARD_ACTOR_CODE_ID, diff --git a/vm/interpreter/Cargo.toml b/vm/interpreter/Cargo.toml index d2971aef0b75..d10488d61d3c 100644 --- a/vm/interpreter/Cargo.toml +++ b/vm/interpreter/Cargo.toml @@ -24,5 +24,6 @@ log = "0.4.8" db = { path = "../../node/db" } chain = { path = "../../blockchain/chain" } fil_types = { path = "../../types" } + [dev-dependencies] ipld_hamt = { path = "../../ipld/hamt" } \ No newline at end of file diff --git a/vm/interpreter/src/default_runtime.rs b/vm/interpreter/src/default_runtime.rs index 38142da06ba4..62c3d7219b12 100644 --- a/vm/interpreter/src/default_runtime.rs +++ b/vm/interpreter/src/default_runtime.rs @@ -5,49 +5,74 @@ use super::gas_block_store::GasBlockStore; use super::gas_syscalls::GasSyscalls; use super::gas_tracker::{price_list_by_epoch, GasTracker, PriceList}; use super::ChainRand; -use actor::{ - self, account, ACCOUNT_ACTOR_CODE_ID, CRON_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID, - MARKET_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID, - POWER_ACTOR_CODE_ID, REWARD_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, VERIFIED_ACTOR_CODE_ID, -}; +use actor::*; use address::{Address, Protocol}; use byteorder::{BigEndian, WriteBytesExt}; use cid::{multihash::Blake2b256, Cid}; use clock::ChainEpoch; use crypto::DomainSeparationTag; -use forest_encoding::to_vec; +use fil_types::NetworkParams; use forest_encoding::Cbor; +use forest_encoding::{error::Error as EncodingError, to_vec}; use ipld_blockstore::BlockStore; +use log::warn; use message::{Message, UnsignedMessage}; use num_bigint::BigInt; use runtime::{ActorCode, MessageInfo, Runtime, Syscalls}; use state_tree::StateTree; use std::cell::RefCell; +use std::marker::PhantomData; use std::rc::Rc; use vm::{ actor_error, ActorError, ActorState, ExitCode, MethodNum, Randomness, Serialized, TokenAmount, - METHOD_SEND, + EMPTY_ARR_CID, METHOD_SEND, }; +// TODO this param isn't finalized +const ACTOR_EXEC_GAS: i64 = 0; + +struct VMMsg { + caller: Address, + receiver: Address, + value_received: TokenAmount, +} + +impl MessageInfo for VMMsg { + fn caller(&self) -> &Address { + &self.caller + } + fn receiver(&self) -> &Address { + &self.receiver + } + fn value_received(&self) -> &TokenAmount { + &self.value_received + } +} + /// Implementation of the Runtime trait. -pub struct DefaultRuntime<'db, 'msg, 'st, 'sys, 'r, BS, SYS> { +pub struct DefaultRuntime<'db, 'msg, 'st, 'sys, 'r, BS, SYS, P> { state: &'st mut StateTree<'db, BS>, store: GasBlockStore<'db, BS>, syscalls: GasSyscalls<'sys, SYS>, gas_tracker: Rc>, message: &'msg UnsignedMessage, + vm_msg: VMMsg, epoch: ChainEpoch, origin: Address, origin_nonce: u64, num_actors_created: u64, price_list: PriceList, rand: &'r ChainRand, + caller_validated: bool, + allow_internal: bool, + params: PhantomData

, } -impl<'db, 'msg, 'st, 'sys, 'r, BS, SYS> DefaultRuntime<'db, 'msg, 'st, 'sys, 'r, BS, SYS> +impl<'db, 'msg, 'st, 'sys, 'r, BS, SYS, P> DefaultRuntime<'db, 'msg, 'st, 'sys, 'r, BS, SYS, P> where BS: BlockStore, SYS: Syscalls, + P: NetworkParams, { /// Constructs a new Runtime #[allow(clippy::too_many_arguments)] @@ -62,12 +87,9 @@ where origin_nonce: u64, num_actors_created: u64, rand: &'r ChainRand, - ) -> Self { + ) -> Result { let price_list = price_list_by_epoch(epoch); - let gas_tracker = Rc::new(RefCell::new(GasTracker::new( - message.gas_limit() as i64, - gas_used, - ))); + let gas_tracker = Rc::new(RefCell::new(GasTracker::new(message.gas_limit(), gas_used))); let gas_block_store = GasBlockStore { price_list, gas: Rc::clone(&gas_tracker), @@ -78,19 +100,37 @@ where gas: Rc::clone(&gas_tracker), syscalls, }; - DefaultRuntime { + + let caller_id = state + .lookup_id(&message.from()) + .map_err(|e| actor_error!(fatal("failed to lookup id: {}", e)))? + .ok_or_else( + || actor_error!(SysErrInvalidReceiver; "resolve msg from address failed"), + )?; + + let vm_msg = VMMsg { + caller: caller_id, + receiver: *message.receiver(), + value_received: message.value_received().clone(), + }; + + Ok(DefaultRuntime { state, store: gas_block_store, syscalls: gas_syscalls, gas_tracker, message, + vm_msg, epoch, origin, origin_nonce, num_actors_created, price_list, rand, - } + allow_internal: true, + caller_validated: false, + params: PhantomData, + }) } /// Adds to amount of used @@ -114,105 +154,216 @@ where self.price_list } - /// Gets the specified Actor from the state tree - fn get_actor(&self, addr: &Address) -> Result { - // TODO handle exit codes specifically, this leads to a broken implementation - self.state - .get_actor(&addr) - .map_err(|e| { - self.abort( - ExitCode::SysErrInternal, - format!("failed to load actor: {}", e), - ) - })? - .ok_or_else(|| self.abort(ExitCode::SysErrInternal, "actor not found")) - } - /// Get the balance of a particular Actor from their Address fn get_balance(&self, addr: &Address) -> Result { - // TODO fix this, not found should return 0 not error, on error should turn error into fatal - self.get_actor(&addr).map(|act| act.balance) + Ok(self + .state + .get_actor(&addr) + .map_err(ActorError::new_fatal)? + .map(|act| act.balance) + .unwrap_or_default()) } /// Update the state Cid of the Message receiver fn state_commit(&mut self, old_h: &Cid, new_h: Cid) -> Result<(), ActorError> { let to_addr = *self.message().receiver(); - let mut actor = self.get_actor(&to_addr)?; + let mut actor = self + .state + .get_actor(&to_addr) + .map_err(ActorError::new_fatal)? + .ok_or_else(|| actor_error!(fatal("failed to get actor to commit state")))?; if &actor.state != old_h { - return Err(self.abort( - ExitCode::ErrIllegalState, - "failed to update, inconsistent base reference".to_owned(), - )); + return Err(actor_error!(fatal( + "failed to update, inconsistent base reference" + ))); } actor.state = new_h; - self.state.set_actor(&to_addr, actor).map_err(|e| { - self.abort( - ExitCode::SysErrInternal, - format!("failed to set actor in state_commit: {}", e), - ) - })?; + self.state + .set_actor(&to_addr, actor) + .map_err(|e| actor_error!(fatal("failed to set actor in state_commit: {}", e)))?; Ok(()) } + + fn abort_if_already_validated(&mut self) -> Result<(), ActorError> { + if self.caller_validated { + Err(actor_error!(SysErrorIllegalActor; + "Method must validate caller identity exactly once")) + } else { + self.caller_validated = true; + Ok(()) + } + } + + /// Helper function for inserting into blockstore. + fn put(&self, obj: &T) -> Result + where + T: Cbor, + { + self.store + .put(obj, Blake2b256) + .map_err(|e| match e.downcast::() { + Ok(ser_error) => actor_error!(ErrSerialization; + "failed to marshal cbor object {}", ser_error), + Err(other) => actor_error!(fatal("failed to put cbor object: {}", other)), + }) + } + + /// Helper function for getting deserializable objects from blockstore. + fn get(&self, cid: &Cid) -> Result, ActorError> + where + T: Cbor, + { + self.store + .get(cid) + .map_err(|e| match e.downcast::() { + Ok(ser_error) => actor_error!(ErrSerialization; + "failed to unmarshal cbor object {}", ser_error), + Err(other) => actor_error!(fatal("failed to get cbor object: {}", other)), + }) + } + + fn internal_send( + &mut self, + from: Address, + to: Address, + method: MethodNum, + value: TokenAmount, + params: Serialized, + ) -> Result { + let msg = UnsignedMessage::builder() + .from(from) + .to(to) + .method_num(method) + .value(value) + .params(params) + .gas_limit(self.gas_available()) + .build() + .expect("Message creation fails"); + + // snapshot state tree + let snapshot = self + .state + .snapshot() + .map_err(|e| actor_error!(fatal("failed to create snapshot {}", e)))?; + + let send_res = vm_send::(self, &msg, None); + send_res.map_err(|e| { + if let Err(e) = self.state.revert_to_snapshot(&snapshot) { + actor_error!(fatal("failed to revert snapshot: {}", e)) + } else { + e + } + }) + } + + /// creates account actors from only BLS/SECP256K1 addresses. + pub fn try_create_account_actor(&mut self, addr: &Address) -> Result { + self.charge_gas(self.price_list().on_create_actor())?; + + let addr_id = self + .state + .register_new_address(addr) + .map_err(ActorError::new_fatal)?; + + let act = make_actor(addr)?; + + self.state + .set_actor(&addr_id, act) + .map_err(ActorError::new_fatal)?; + + let p = Serialized::serialize(&addr).map_err(|e| { + actor_error!(fatal( + "couldn't serialize params for actor construction: {}", + e + )) + })?; + + self.internal_send( + *SYSTEM_ACTOR_ADDR, + addr_id, + account::Method::Constructor as u64, + TokenAmount::from(0), + p, + )?; + + let act = self + .state + .get_actor(&addr_id) + .map_err(ActorError::new_fatal)? + .ok_or_else(|| actor_error!(fatal("failed to retrieve created actor state")))?; + + Ok(act) + } } -impl Runtime for DefaultRuntime<'_, '_, '_, '_, '_, BS, SYS> +impl Runtime for DefaultRuntime<'_, '_, '_, '_, '_, BS, SYS, P> where BS: BlockStore, SYS: Syscalls, + P: NetworkParams, { fn message(&self) -> &dyn MessageInfo { - self.message + &self.vm_msg } fn curr_epoch(&self) -> ChainEpoch { self.epoch } - fn validate_immediate_caller_accept_any(&self) {} - fn validate_immediate_caller_is<'db, I>(&self, addresses: I) -> Result<(), ActorError> + fn validate_immediate_caller_accept_any(&mut self) -> Result<(), ActorError> { + self.abort_if_already_validated() + } + fn validate_immediate_caller_is<'db, I>(&mut self, addresses: I) -> Result<(), ActorError> where I: IntoIterator, { - let imm = self.resolve_address(self.message().caller())?; + self.abort_if_already_validated()?; + + let imm = self.message().caller(); // Check if theres is at least one match - if !addresses.into_iter().any(|a| *a == imm) { - return Err(self.abort( - ExitCode::SysErrForbidden, - format!("caller is not one of {}", self.message().caller()), + if !addresses.into_iter().any(|a| a == imm) { + return Err(actor_error!(SysErrForbidden; + "caller {} is not one of supported", self.message().caller() )); } Ok(()) } - fn validate_immediate_caller_type<'db, I>(&self, types: I) -> Result<(), ActorError> + fn validate_immediate_caller_type<'db, I>(&mut self, types: I) -> Result<(), ActorError> where I: IntoIterator, { - let caller_cid = self.get_actor_code_cid(self.message().receiver())?; - if types.into_iter().any(|c| *c == caller_cid) { - return Err(self.abort( - ExitCode::SysErrForbidden, - format!( - "caller cid type {} one of {}", - caller_cid, - self.message().caller() - ), - )); + self.abort_if_already_validated()?; + + let caller_cid = self + .get_actor_code_cid(self.message().caller())? + .ok_or_else(|| actor_error!(fatal("failed to lookup code cid for caller")))?; + if !types.into_iter().any(|c| *c == caller_cid) { + return Err(actor_error!(SysErrForbidden; + "caller cid type {} not one of supported", caller_cid)); } Ok(()) } + fn current_balance(&self) -> Result { self.get_balance(self.message.to()) } - fn resolve_address(&self, address: &Address) -> Result { + + fn resolve_address(&self, address: &Address) -> Result, ActorError> { self.state .lookup_id(&address) - .map_err(|e| self.abort(ExitCode::ErrPlaceholder, e)) + .map_err(ActorError::new_fatal) } - fn get_actor_code_cid(&self, addr: &Address) -> Result { - self.get_actor(&addr).map(|act| act.code) + + fn get_actor_code_cid(&self, addr: &Address) -> Result, ActorError> { + Ok(self + .state + .get_actor(&addr) + .map_err(ActorError::new_fatal)? + .map(|act| act.code)) } + fn get_randomness( &self, personalization: DomainSeparationTag, @@ -228,31 +379,30 @@ where } fn create(&mut self, obj: &C) -> Result<(), ActorError> { - let c = self.store.put(obj, Blake2b256).map_err(|e| { - self.abort( - ExitCode::ErrPlaceholder, - format!("storage put in create: {}", e.to_string()), - ) - })?; - // TODO: This is almost certainly wrong. Need to CBOR an empty slice and calculate Cid - self.state_commit(&Cid::default(), c) + let c = self.put(obj)?; + + self.state_commit(&EMPTY_ARR_CID, c) } + fn state(&self) -> Result { - let actor = self.get_actor(self.message().receiver())?; - self.store - .get(&actor.state) + let actor = self + .state + .get_actor(self.message().receiver()) .map_err(|e| { - self.abort( - ExitCode::ErrPlaceholder, - format!("storage get error in read only state: {}", e.to_string()), - ) + actor_error!(SysErrorIllegalArgument; + "failed to get actor for Readonly state: {}", e) })? - .ok_or_else(|| { - self.abort( - ExitCode::ErrPlaceholder, - "storage get error in read only state".to_owned(), - ) - }) + .ok_or_else( + || actor_error!(SysErrorIllegalArgument; "Actor readonly state does not exist"), + )?; + + // TODO revisit as the go impl doesn't handle not exists and nil cases + self.get(&actor.state)?.ok_or_else(|| { + actor_error!(fatal( + "State does not exist for actor state cid: {}", + actor.state + )) + }) } fn transaction(&mut self, f: F) -> Result @@ -261,34 +411,23 @@ where F: FnOnce(&mut C, &mut Self) -> R, { // get actor - let act = self.get_actor(self.message().receiver())?; + let act = self.state.get_actor(self.message().receiver()) + .map_err(|e| actor_error!(SysErrorIllegalActor; "failed to get actor for transaction: {}", e))? + .ok_or_else(|| actor_error!(SysErrorIllegalActor; + "actor state for transaction doesn't exist"))?; // get state for actor based on generic C + // TODO Lotus is not handling the not exist case, revisit let mut state: C = self - .store - .get(&act.state) - .map_err(|e| { - self.abort( - ExitCode::ErrPlaceholder, - format!("storage get error in transaction: {}", e.to_string()), - ) - })? - .ok_or_else(|| { - self.abort( - ExitCode::ErrPlaceholder, - "storage get error in transaction".to_owned(), - ) - })?; + .get(&act.state)? + .ok_or_else(|| actor_error!(fatal("Actor state does not exist: {}", act.state)))?; // Update the state + self.allow_internal = false; let r = f(&mut state, self); + self.allow_internal = true; - let c = self.store.put(&state, Blake2b256).map_err(|e| { - self.abort( - ExitCode::ErrPlaceholder, - format!("storage put in create: {}", e.to_string()), - ) - })?; + let c = self.put(&state)?; // Committing that change self.state_commit(&act.state, c)?; @@ -301,49 +440,27 @@ where fn send( &mut self, - to: &Address, + to: Address, method: MethodNum, - params: &Serialized, - value: &TokenAmount, + params: Serialized, + value: TokenAmount, ) -> Result { - let msg = UnsignedMessage::builder() - .to(*to) - .from(*self.message.from()) - .method_num(method) - .value(value.clone()) - .gas_limit(self.gas_available() as u64) - .params(params.clone()) - .build() - .unwrap(); - - // snapshot state tree - let snapshot = self - .state - .snapshot() - .map_err(|_e| self.abort(ExitCode::ErrPlaceholder, "failed to create snapshot"))?; - - let epoch = self.curr_epoch(); - let send_res = { - let mut parent = DefaultRuntime::new( - self.state, - self.store.store, - self.syscalls.syscalls, - self.gas_used(), - &msg, - epoch, - self.origin, - self.origin_nonce, - self.num_actors_created, - self.rand, - ); - internal_send::(&mut parent, &msg, 0) - }; - if send_res.is_err() { - self.state - .revert_to_snapshot(&snapshot) - .map_err(|_e| self.abort(ExitCode::ErrPlaceholder, "failed to revert snapshot"))?; + if !self.allow_internal { + return Err(actor_error!(SysErrorIllegalActor; "runtime.send() is not allowed")); } - send_res + + let ret = self + .internal_send(*self.message.receiver(), to, method, value, params) + .map_err(|e| { + warn!( + "internal send failed: (to: {}) (method: {}) {}", + to, method, e + ); + e + })?; + self.charge_gas(ACTOR_EXEC_GAS)?; + + Ok(ret) } fn abort>(&self, exit_code: ExitCode, msg: S) -> ActorError { @@ -393,7 +510,14 @@ where } fn delete_actor(&mut self, _beneficiary: &Address) -> Result<(), ActorError> { self.charge_gas(self.price_list.on_delete_actor())?; - let balance = self.get_actor(self.message.to()).map(|act| act.balance)?; + let balance = self + .state + .get_actor(self.message.to()) + .map_err(|e| actor_error!(fatal("failed to get actor {}, {}", self.message.to(), e)))? + .ok_or_else( + || actor_error!(SysErrorIllegalActor; "failed to load actor in delete actor"), + ) + .map(|act| act.balance)?; if !balance.eq(&0u64.into()) { return Err(self.abort( ExitCode::SysErrInternal, @@ -411,80 +535,97 @@ where &self.syscalls } fn total_fil_circ_supply(&self) -> Result { - todo!() + let get_actor_state = |addr: &Address| -> Result { + self.state + .get_actor(&addr) + .map_err(|e| { + actor_error!(ErrIllegalState; + "failed to get reward actor for cumputing total supply: {}", e) + })? + .ok_or_else( + || actor_error!(ErrIllegalState; "Actor address ({}) does not exist", addr), + ) + }; + + let rew = get_actor_state(&REWARD_ACTOR_ADDR)?; + let burnt = get_actor_state(&BURNT_FUNDS_ACTOR_ADDR)?; + let market = get_actor_state(&STORAGE_MARKET_ACTOR_ADDR)?; + let power = get_actor_state(&STORAGE_POWER_ACTOR_ADDR)?; + + let st: power::State = self + .store + .get(&power.state) + .map_err(|e| { + actor_error!(ErrIllegalState; + "failed to get storage power state: {}", e.to_string()) + })? + .ok_or_else(|| actor_error!(ErrIllegalState; "Failed to retrieve power state"))?; + + let total = P::from_fil(P::TOTAL_FILECOIN) + - rew.balance + - market.balance + - burnt.balance + - st.total_pledge_collateral; + Ok(total) } } + /// Shared logic between the DefaultRuntime and the Interpreter. /// It invokes methods on different Actors based on the Message. -pub fn internal_send( - runtime: &mut DefaultRuntime<'_, '_, '_, '_, '_, BS, SYS>, +pub fn vm_send<'db, 'msg, 'st, 'sys, 'r, BS, SYS, P>( + rt: &mut DefaultRuntime<'db, 'msg, 'st, 'sys, 'r, BS, SYS, P>, msg: &UnsignedMessage, - _gas_cost: i64, + gas_cost: Option, ) -> Result where BS: BlockStore, SYS: Syscalls, + P: NetworkParams, { - runtime.charge_gas( - runtime - .price_list() + if let Some(cost) = gas_cost { + rt.charge_gas(cost)?; + } + + // TODO maybe move this + rt.charge_gas( + rt.price_list() .on_method_invocation(msg.value(), msg.method_num()), )?; - // TODO: we need to try to recover here and try to create account actor - let to_actor = runtime.get_actor(msg.to())?; - - if msg.value() != &0u8.into() { - transfer(runtime.state, &msg.from(), &msg.to(), &msg.value()) - .map_err(|e| actor_error!(SysErrSenderInvalid; e))?; - } - - let method_num = msg.method_num(); - - if method_num != METHOD_SEND { - let ret = { - // TODO: make its own method/struct - match to_actor.code { - x if x == *SYSTEM_ACTOR_CODE_ID => { - actor::system::Actor.invoke_method(runtime, method_num, msg.params()) - } - x if x == *INIT_ACTOR_CODE_ID => { - actor::init::Actor.invoke_method(runtime, method_num, msg.params()) - } - x if x == *CRON_ACTOR_CODE_ID => { - actor::cron::Actor.invoke_method(runtime, method_num, msg.params()) - } - x if x == *ACCOUNT_ACTOR_CODE_ID => { - actor::account::Actor.invoke_method(runtime, method_num, msg.params()) - } - x if x == *POWER_ACTOR_CODE_ID => { - actor::power::Actor.invoke_method(runtime, method_num, msg.params()) - } - x if x == *MINER_ACTOR_CODE_ID => { - actor::miner::Actor.invoke_method(runtime, method_num, msg.params()) - } - x if x == *MARKET_ACTOR_CODE_ID => { - actor::market::Actor.invoke_method(runtime, method_num, msg.params()) - } - x if x == *PAYCH_ACTOR_CODE_ID => { - actor::paych::Actor.invoke_method(runtime, method_num, msg.params()) - } - x if x == *MULTISIG_ACTOR_CODE_ID => { - actor::multisig::Actor.invoke_method(runtime, method_num, msg.params()) - } - x if x == *REWARD_ACTOR_CODE_ID => { - actor::reward::Actor.invoke_method(runtime, method_num, msg.params()) - } - x if x == *VERIFIED_ACTOR_CODE_ID => { - actor::verifreg::Actor.invoke_method(runtime, method_num, msg.params()) - } - _ => Err( - actor_error!(SysErrorIllegalActor; "no code for actor at address {}", msg.to()), - ), + { + // On get actor gas charge + // TODO this value shouldn't be final + rt.charge_gas(0)?; + + // TODO: we need to try to recover here and try to create account actor + // TODO: actually fix this and don't leave as unwrap for PR + let to_actor = match rt + .state + .get_actor(msg.to()) + .map_err(ActorError::new_fatal)? + { + Some(act) => act, + None => { + // Try to create actor if not exist + rt.try_create_account_actor(msg.to())? } }; - return ret; + + rt.charge_gas( + rt.price_list() + .on_method_invocation(msg.value(), msg.method_num()), + )?; + + if msg.value() > &TokenAmount::from(0) { + transfer(rt.state, &msg.from(), &msg.to(), &msg.value())?; + } + + if msg.method_num() != METHOD_SEND { + rt.charge_gas(ACTOR_EXEC_GAS)?; + return invoke(rt, to_actor.code, msg.method_num(), msg.params(), msg.to()); + } } + Ok(Serialized::default()) } @@ -494,31 +635,90 @@ fn transfer( from: &Address, to: &Address, value: &TokenAmount, -) -> Result<(), String> { +) -> Result<(), ActorError> { if from == to { return Ok(()); } - if value < &0u8.into() { - return Err("Negative transfer value".to_owned()); + + let from_id = state + .lookup_id(from) + .map_err(ActorError::new_fatal)? + .ok_or_else(|| actor_error!(fatal("Failed to lookup from id for address {}", from)))?; + let to_id = state + .lookup_id(to) + .map_err(ActorError::new_fatal)? + .ok_or_else(|| actor_error!(fatal("Failed to lookup to id for address {}", to)))?; + + if from_id == to_id { + return Ok(()); + } + + if value < &0.into() { + return Err( + actor_error!(SysErrForbidden; "attempted to transfer negative transfer value {}", value), + ); } let mut f = state - .get_actor(from)? - .ok_or("Transfer failed when retrieving sender actor")?; + .get_actor(&from_id) + .map_err(ActorError::new_fatal)? + .ok_or_else(|| { + actor_error!(fatal( + "sender actor does not exist in state during transfer" + )) + })?; let mut t = state - .get_actor(to)? - .ok_or("Transfer failed when retrieving receiver actor")?; + .get_actor(&to_id) + .map_err(ActorError::new_fatal)? + .ok_or_else(|| { + actor_error!(fatal( + "receiver actor does not exist in state during transfer" + )) + })?; - f.deduct_funds(&value)?; + f.deduct_funds(&value).map_err(|e| { + actor_error!(SysErrInsufficientFunds; + "transfer failed when deducting funds ({}): {}", value, e) + })?; t.deposit_funds(&value); - state.set_actor(from, f)?; - state.set_actor(to, t)?; + state.set_actor(from, f).map_err(ActorError::new_fatal)?; + state.set_actor(to, t).map_err(ActorError::new_fatal)?; Ok(()) } -/// Returns public address of the specified actor address +/// Calls actor code with method and parameters. +fn invoke<'db, 'msg, 'st, 'sys, 'r, BS, SYS, P>( + rt: &mut DefaultRuntime<'db, 'msg, 'st, 'sys, 'r, BS, SYS, P>, + code: Cid, + method_num: MethodNum, + params: &Serialized, + to: &Address, +) -> Result +where + BS: BlockStore, + SYS: Syscalls, + P: NetworkParams, +{ + match code { + x if x == *SYSTEM_ACTOR_CODE_ID => system::Actor.invoke_method(rt, method_num, params), + x if x == *INIT_ACTOR_CODE_ID => init::Actor.invoke_method(rt, method_num, params), + x if x == *CRON_ACTOR_CODE_ID => cron::Actor.invoke_method(rt, method_num, params), + x if x == *ACCOUNT_ACTOR_CODE_ID => account::Actor.invoke_method(rt, method_num, params), + x if x == *POWER_ACTOR_CODE_ID => power::Actor.invoke_method(rt, method_num, params), + x if x == *MINER_ACTOR_CODE_ID => miner::Actor.invoke_method(rt, method_num, params), + x if x == *MARKET_ACTOR_CODE_ID => market::Actor.invoke_method(rt, method_num, params), + x if x == *PAYCH_ACTOR_CODE_ID => paych::Actor.invoke_method(rt, method_num, params), + x if x == *MULTISIG_ACTOR_CODE_ID => multisig::Actor.invoke_method(rt, method_num, params), + x if x == *REWARD_ACTOR_CODE_ID => reward::Actor.invoke_method(rt, method_num, params), + x if x == *VERIFREG_ACTOR_CODE_ID => verifreg::Actor.invoke_method(rt, method_num, params), + _ => Err(actor_error!(SysErrorIllegalActor; "no code for actor at address {}", to)), + } +} + +/// returns the public key type of address (`BLS`/`SECP256K1`) of an account actor +/// identified by `addr`. pub fn resolve_to_key_addr<'st, 'bs, BS, S>( st: &'st StateTree<'bs, S>, store: &'bs BS, @@ -561,3 +761,32 @@ where Ok(acc_st.address) } + +fn make_actor(addr: &Address) -> Result { + match addr.protocol() { + Protocol::BLS => Ok(new_bls_account_actor()), + Protocol::Secp256k1 => Ok(new_secp256k1_account_actor()), + Protocol::ID => { + Err(actor_error!(SysErrInvalidReceiver; "no actor with given id: {}", addr)) + } + Protocol::Actor => Err(actor_error!(SysErrInvalidReceiver; "no such actor: {}", addr)), + } +} + +fn new_bls_account_actor() -> ActorState { + ActorState { + code: ACCOUNT_ACTOR_CODE_ID.clone(), + balance: TokenAmount::from(0), + state: EMPTY_ARR_CID.clone(), + sequence: 0, + } +} + +fn new_secp256k1_account_actor() -> ActorState { + ActorState { + code: ACCOUNT_ACTOR_CODE_ID.clone(), + balance: TokenAmount::from(0), + state: EMPTY_ARR_CID.clone(), + sequence: 0, + } +} diff --git a/vm/interpreter/src/gas_block_store.rs b/vm/interpreter/src/gas_block_store.rs index 235ecef6f144..797e275b466f 100644 --- a/vm/interpreter/src/gas_block_store.rs +++ b/vm/interpreter/src/gas_block_store.rs @@ -9,6 +9,7 @@ use ipld_blockstore::BlockStore; use std::cell::RefCell; use std::error::Error as StdError; use std::rc::Rc; +use vm::{actor_error, ActorError}; /// Blockstore wrapper to charge gas on reads and writes pub(crate) struct GasBlockStore<'bs, BS> { @@ -23,8 +24,10 @@ where { /// Get bytes from block store by Cid fn get_bytes(&self, cid: &Cid) -> Result>, Box> { - // TODO investigate if should panic/exit here, should be fatal - let ret = self.store.get_bytes(cid)?; + let ret = self + .store + .get_bytes(cid) + .map_err(|e| actor_error!(fatal("failed to get block from blockstore: {}", e)))?; if let Some(bz) = &ret { self.gas .borrow_mut() @@ -54,8 +57,10 @@ where .borrow_mut() .charge_gas(self.price_list.on_ipld_put(to_vec(obj).unwrap().len()))?; - // TODO investigate if error here should be fatal - self.store.put(obj, hash) + Ok(self + .store + .put(obj, hash) + .map_err(|e| actor_error!(fatal("failed to write to store {}", e)))?) } } diff --git a/vm/interpreter/src/gas_syscalls.rs b/vm/interpreter/src/gas_syscalls.rs index 56c80a63b25c..ca9f81112ef8 100644 --- a/vm/interpreter/src/gas_syscalls.rs +++ b/vm/interpreter/src/gas_syscalls.rs @@ -29,20 +29,16 @@ where signer: &Address, plaintext: &[u8], ) -> Result<(), Box> { - self.gas - .borrow_mut() - .charge_gas( - self.price_list - .on_verify_signature(signature.signature_type(), plaintext.len()), - ) - .unwrap(); + self.gas.borrow_mut().charge_gas( + self.price_list + .on_verify_signature(signature.signature_type(), plaintext.len()), + )?; self.syscalls.verify_signature(signature, signer, plaintext) } fn hash_blake2b(&self, data: &[u8]) -> Result<[u8; 32], Box> { self.gas .borrow_mut() - .charge_gas(self.price_list.on_hashing(data.len())) - .unwrap(); + .charge_gas(self.price_list.on_hashing(data.len()))?; self.syscalls.hash_blake2b(data) } fn compute_unsealed_sector_cid( @@ -52,22 +48,19 @@ where ) -> Result> { self.gas .borrow_mut() - .charge_gas(self.price_list.on_compute_unsealed_sector_cid(reg, pieces)) - .unwrap(); + .charge_gas(self.price_list.on_compute_unsealed_sector_cid(reg, pieces))?; self.syscalls.compute_unsealed_sector_cid(reg, pieces) } fn verify_seal(&self, vi: &SealVerifyInfo) -> Result<(), Box> { self.gas .borrow_mut() - .charge_gas(self.price_list.on_verify_seal(vi)) - .unwrap(); + .charge_gas(self.price_list.on_verify_seal(vi))?; self.syscalls.verify_seal(vi) } fn verify_post(&self, vi: &WindowPoStVerifyInfo) -> Result<(), Box> { self.gas .borrow_mut() - .charge_gas(self.price_list.on_verify_post(vi)) - .unwrap(); + .charge_gas(self.price_list.on_verify_post(vi))?; self.syscalls.verify_post(vi) } fn verify_consensus_fault( @@ -78,8 +71,7 @@ where ) -> Result, Box> { self.gas .borrow_mut() - .charge_gas(self.price_list.on_verify_consensus_fault()) - .unwrap(); + .charge_gas(self.price_list.on_verify_consensus_fault())?; self.syscalls.verify_consensus_fault(h1, h2, extra) } diff --git a/vm/interpreter/src/gas_tracker/mod.rs b/vm/interpreter/src/gas_tracker/mod.rs index 49f8c1c447ab..eae09864fd49 100644 --- a/vm/interpreter/src/gas_tracker/mod.rs +++ b/vm/interpreter/src/gas_tracker/mod.rs @@ -24,9 +24,8 @@ impl GasTracker { if self.gas_used + to_use > self.gas_available { self.gas_used = self.gas_available; Err(actor_error!(SysErrOutOfGas; - "not enough gas (used={}) (available={})", - self.gas_used + to_use, - self.gas_available + "not enough gas (used={}) (available={})", + self.gas_used + to_use, self.gas_available )) } else { self.gas_used += to_use; diff --git a/vm/interpreter/src/gas_tracker/price_list.rs b/vm/interpreter/src/gas_tracker/price_list.rs index 501c2fa7ba35..1761a62a1b8f 100644 --- a/vm/interpreter/src/gas_tracker/price_list.rs +++ b/vm/interpreter/src/gas_tracker/price_list.rs @@ -77,8 +77,8 @@ pub struct PriceList { impl PriceList { /// Returns the gas required for storing a message of a given size in the chain. #[inline] - pub fn on_chain_message(&self, msg_size: i64) -> i64 { - self.on_chain_message_base + self.on_chain_message_per_byte * msg_size + pub fn on_chain_message(&self, msg_size: usize) -> i64 { + self.on_chain_message_base + self.on_chain_message_per_byte * msg_size as i64 } /// Returns the gas required for storing the response of a message in the chain. #[inline] diff --git a/vm/interpreter/src/vm.rs b/vm/interpreter/src/vm.rs index 8901ab3cf20a..234ccc49b076 100644 --- a/vm/interpreter/src/vm.rs +++ b/vm/interpreter/src/vm.rs @@ -1,13 +1,14 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use super::{gas_tracker::price_list_by_epoch, internal_send, ChainRand, DefaultRuntime}; +use super::{gas_tracker::price_list_by_epoch, vm_send, ChainRand, DefaultRuntime}; use actor::{ cron, reward, ACCOUNT_ACTOR_CODE_ID, CRON_ACTOR_ADDR, REWARD_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }; use blocks::FullTipset; use cid::Cid; use clock::ChainEpoch; +use fil_types::NetworkParams; use forest_encoding::Cbor; use ipld_blockstore::BlockStore; use log::warn; @@ -18,24 +19,25 @@ use runtime::Syscalls; use state_tree::StateTree; use std::collections::HashSet; use std::error::Error as StdError; +use std::marker::PhantomData; use vm::{actor_error, ActorError, ExitCode, Serialized}; /// Interpreter which handles execution of state transitioning messages and returns receipts /// from the vm execution. -pub struct VM<'db, 'r, DB, SYS> { +pub struct VM<'db, 'r, DB, SYS, P> { state: StateTree<'db, DB>, - // TODO revisit handling buffered store specifically in VM store: &'db DB, epoch: ChainEpoch, syscalls: SYS, rand: &'r ChainRand, - // TODO: missing fields + params: PhantomData

, } -impl<'db, 'r, DB, SYS> VM<'db, 'r, DB, SYS> +impl<'db, 'r, DB, SYS, P> VM<'db, 'r, DB, SYS, P> where DB: BlockStore, SYS: Syscalls, + P: NetworkParams, { pub fn new( root: &Cid, @@ -51,6 +53,7 @@ where epoch, syscalls, rand, + params: PhantomData, }) } @@ -66,7 +69,7 @@ where /// Apply all messages from a tipset /// Returns the receipts from the transactions. - pub fn apply_tip_set_messages( + pub fn apply_tipset_messages( &mut self, tipset: &FullTipset, mut callback: Option Result<(), String>>, @@ -127,8 +130,8 @@ where .value(BigInt::zero()) .gas_price(BigInt::zero()) .gas_limit(1 << 30) - .method_num(reward::Method::AwardBlockReward as u64) .params(params) + .method_num(reward::Method::AwardBlockReward as u64) .build()?; // TODO revisit this ApplyRet structure, doesn't match go logic 1:1 and can be cleaner @@ -176,29 +179,21 @@ where } pub fn apply_implicit_message(&mut self, msg: &UnsignedMessage) -> ApplyRet { - let (ret_data, _, act_err) = self.send(msg, 0); - - if let Some(err) = act_err { - return ApplyRet::new( - MessageReceipt { - return_data: ret_data, - exit_code: err.exit_code(), - gas_used: 0, + let (return_data, _, act_err) = self.send(msg, None); + + ApplyRet { + msg_receipt: MessageReceipt { + return_data, + exit_code: if let Some(err) = act_err { + err.exit_code() + } else { + ExitCode::Ok }, - BigInt::zero(), - Some(err), - ); - }; - - ApplyRet::new( - MessageReceipt { - return_data: ret_data, - exit_code: ExitCode::Ok, gas_used: 0, }, - BigInt::zero(), - None, - ) + act_error: None, + penalty: BigInt::zero(), + } } /// Applies the state transition for a single message @@ -208,73 +203,76 @@ where let pl = price_list_by_epoch(self.epoch()); let ser_msg = &msg.marshal_cbor().map_err(|e| e.to_string())?; - let msg_gas_cost = pl.on_chain_message(ser_msg.len() as i64) as u64; + let msg_gas_cost = pl.on_chain_message(ser_msg.len()); if msg_gas_cost > msg.gas_limit() { - return Ok(ApplyRet::new( - MessageReceipt { - return_data: Serialized::default(), + return Ok(ApplyRet { + msg_receipt: MessageReceipt { + return_data: Serialized::empty(), exit_code: ExitCode::SysErrOutOfGas, gas_used: 0, }, - msg.gas_price() * msg_gas_cost, - Some(actor_error!(SysErrOutOfGas; "Out of gas")), - )); + act_error: Some(actor_error!(SysErrOutOfGas; + "Out of gas ({} > {})", msg_gas_cost, msg.gas_limit())), + penalty: msg.gas_price() * msg_gas_cost, + }); } let miner_penalty_amount = msg.gas_price() * msg_gas_cost; let mut from_act = match self.state.get_actor(msg.from()) { Ok(from_act) => from_act.ok_or("Failed to retrieve actor state")?, Err(_) => { - return Ok(ApplyRet::new( - MessageReceipt { - return_data: Serialized::default(), + return Ok(ApplyRet { + msg_receipt: MessageReceipt { + return_data: Serialized::empty(), exit_code: ExitCode::SysErrSenderInvalid, gas_used: 0, }, - msg.gas_price() * msg_gas_cost, - Some(actor_error!(SysErrSenderInvalid; "Sender invalid")), - )); + penalty: msg.gas_price() * msg_gas_cost, + act_error: Some(actor_error!(SysErrSenderInvalid; "Sender invalid")), + }); } }; if from_act.code != *ACCOUNT_ACTOR_CODE_ID { - return Ok(ApplyRet::new( - MessageReceipt { - return_data: Serialized::default(), + return Ok(ApplyRet { + msg_receipt: MessageReceipt { + return_data: Serialized::empty(), exit_code: ExitCode::SysErrSenderInvalid, gas_used: 0, }, - miner_penalty_amount, - Some(actor_error!(SysErrSenderInvalid; "Sender invalid")), - )); + penalty: miner_penalty_amount, + act_error: Some(actor_error!(SysErrSenderInvalid; "send not from account actor")), + }); }; + // TODO revisit if this is removed in future if msg.sequence() != from_act.sequence { - return Ok(ApplyRet::new( - MessageReceipt { - return_data: Serialized::default(), + return Ok(ApplyRet { + msg_receipt: MessageReceipt { + return_data: Serialized::empty(), exit_code: ExitCode::SysErrSenderStateInvalid, gas_used: 0, }, - miner_penalty_amount, - Some(actor_error!(SysErrSenderStateInvalid; "Sender state invalid")), - )); + penalty: miner_penalty_amount, + act_error: Some(actor_error!(SysErrSenderStateInvalid; + "actor sequence invalid: {} != {}", msg.sequence(), from_act.sequence)), + }); }; let gas_cost = msg.gas_price() * msg.gas_limit(); - // TODO requires network_tx_fee to be added as per the spec let total_cost = &gas_cost + msg.value(); if from_act.balance < total_cost { - return Ok(ApplyRet::new( - MessageReceipt { - return_data: Serialized::default(), + return Ok(ApplyRet { + msg_receipt: MessageReceipt { + return_data: Serialized::empty(), exit_code: ExitCode::SysErrSenderStateInvalid, gas_used: 0, }, - miner_penalty_amount, - Some(actor_error!(SysErrSenderStateInvalid; "Sender state invalid")), - )); + penalty: miner_penalty_amount, + act_error: Some(actor_error!(SysErrSenderStateInvalid; + "actor balance less than needed: {} < {}", from_act.balance, total_cost)), + }); }; self.state.mutate_actor(msg.from(), |act| { @@ -285,27 +283,60 @@ where let snapshot = self.state.snapshot()?; - // scoped to deal with mutable reference borrowing - let (ret_data, gas_used, act_err) = { - let (ret_data, mut rt, act_err) = self.send(msg, msg_gas_cost as i64); - rt.charge_gas(rt.price_list().on_chain_return_value(ret_data.len())) - .map_err(|e| e.to_string())?; - (ret_data, rt.gas_used(), act_err) + let (mut ret_data, rt, mut act_err) = self.send(msg, Some(msg_gas_cost)); + if let Some(err) = &act_err { + if err.is_fatal() { + return Err(format!( + "[from={}, to={}, seq={}, m={}, h={}] fatal error: {}", + msg.from(), + msg.to(), + msg.sequence(), + msg.method_num(), + self.epoch, + err + )); + } else { + warn!( + "[from={}, to={}, seq={}, m={}] send error: {}", + msg.from(), + msg.to(), + msg.sequence(), + msg.method_num(), + err + ); + if !ret_data.is_empty() { + return Err(format!( + "message invocation errored, but had a return value anyway: {}", + err + )); + } + } + } + + let gas_used = if let Some(mut rt) = rt { + if !ret_data.is_empty() { + if let Err(e) = rt.charge_gas(rt.price_list().on_chain_return_value(ret_data.len())) + { + act_err = Some(e); + ret_data = Serialized::empty(); + } + } + if rt.gas_used() < 0 { + 0 + } else { + rt.gas_used() + } + } else { + return Err(format!("send returned None runtime: {:?}", act_err)); }; - if let Some(err) = act_err { - if err.is_fatal() { - return Err(format!("Fatal send actor error occurred, err: {:?}", err)); - }; - if err.exit_code() != ExitCode::Ok { - // revert all state changes since snapshot - if let Err(state_err) = self.state.revert_to_snapshot(&snapshot) { - return Err(format!("Revert state failed: {}", state_err)); - }; + if let Some(err) = &act_err { + if !err.is_ok() { + // Revert all state changes on error. + self.state.revert_to_snapshot(&snapshot)?; } - warn!("Send actor error: from:{}, to:{}", msg.from(), msg.to()); } - let gas_used = if gas_used < 0 { 0 } else { gas_used as u64 }; + // refund unused gas let refund = (msg.gas_limit() - gas_used) * msg.gas_price(); self.state.mutate_actor(msg.from(), |act| { @@ -323,31 +354,31 @@ where return Err("Gas handling math is wrong".to_owned()); } - Ok(ApplyRet::new( - MessageReceipt { + Ok(ApplyRet { + msg_receipt: MessageReceipt { return_data: ret_data, exit_code: ExitCode::Ok, gas_used, }, - BigInt::zero(), - None, - )) + penalty: BigInt::zero(), + act_error: None, + }) } /// Instantiates a new Runtime, and calls internal_send to do the execution. fn send<'m>( &mut self, msg: &'m UnsignedMessage, - gas_cost: i64, + gas_cost: Option, ) -> ( Serialized, - DefaultRuntime<'db, 'm, '_, '_, '_, DB, SYS>, + Option>, Option, ) { - let mut rt = DefaultRuntime::new( + let res = DefaultRuntime::new( &mut self.state, self.store, &self.syscalls, - gas_cost, + gas_cost.unwrap_or_default(), &msg, self.epoch, *msg.from(), @@ -356,52 +387,37 @@ where self.rand, ); - let ser = match internal_send(&mut rt, msg, gas_cost) { - Ok(ser) => ser, - Err(actor_err) => return (Serialized::default(), rt, Some(actor_err)), - }; - (ser, rt, None) + match res { + Ok(mut rt) => match vm_send(&mut rt, msg, gas_cost) { + Ok(ser) => (ser, Some(rt), None), + Err(actor_err) => (Serialized::empty(), Some(rt), Some(actor_err)), + }, + Err(e) => (Serialized::empty(), None, Some(e)), + } } } -// TODO remove allow dead_code -#[allow(dead_code)] /// Apply message return data #[derive(Clone)] pub struct ApplyRet { - msg_receipt: MessageReceipt, - penalty: BigInt, - act_error: Option, -} - -impl ApplyRet { - fn new(msg_receipt: MessageReceipt, penalty: BigInt, act_error: Option) -> Self { - Self { - msg_receipt, - penalty, - act_error, - } - } - - pub fn msg_receipt(&self) -> &MessageReceipt { - &self.msg_receipt - } - - pub fn act_error(&self) -> Option<&ActorError> { - self.act_error.as_ref() - } + pub msg_receipt: MessageReceipt, + pub act_error: Option, + pub penalty: BigInt, } /// Does some basic checks on the Message to see if the fields are valid. -fn check_message(msg: &UnsignedMessage) -> Result<(), String> { +fn check_message(msg: &UnsignedMessage) -> Result<(), &'static str> { if msg.gas_limit() == 0 { - return Err("Message has no gas limit set".to_owned()); + return Err("Message has no gas limit set"); + } + if msg.gas_limit() < 0 { + return Err("Message has negative gas limit"); } if msg.value() == &BigInt::zero() { - return Err("Message has no value set".to_owned()); + return Err("Message has no value set"); } if msg.gas_price() == &BigInt::zero() { - return Err("Message has no gas price set".to_owned()); + return Err("Message has no gas price set"); } Ok(()) diff --git a/vm/interpreter/tests/transfer_test.rs b/vm/interpreter/tests/transfer_test.rs index a9e203784c91..6f5bea8d6ec0 100644 --- a/vm/interpreter/tests/transfer_test.rs +++ b/vm/interpreter/tests/transfer_test.rs @@ -6,7 +6,8 @@ use address::Address; use blocks::TipsetKeys; use cid::multihash::{Blake2b256, Identity}; use db::MemoryDB; -use interpreter::{internal_send, ChainRand, DefaultRuntime, DefaultSyscalls}; +use fil_types::DevnetParams; +use interpreter::{vm_send, ChainRand, DefaultRuntime, DefaultSyscalls}; use ipld_blockstore::BlockStore; use ipld_hamt::Hamt; use message::UnsignedMessage; @@ -75,12 +76,10 @@ fn transfer_test() { 0, ); - let actor_addr_1 = state - .register_new_address(&actor_addr_1, actor_state_1) - .unwrap(); - let actor_addr_2 = state - .register_new_address(&actor_addr_2, actor_state_2) - .unwrap(); + let actor_addr_1 = state.register_new_address(&actor_addr_1).unwrap(); + let actor_addr_2 = state.register_new_address(&actor_addr_2).unwrap(); + state.set_actor(&actor_addr_1, actor_state_1).unwrap(); + state.set_actor(&actor_addr_2, actor_state_2).unwrap(); let message = UnsignedMessage::builder() .to(actor_addr_1.clone()) @@ -95,7 +94,7 @@ fn transfer_test() { let default_syscalls = DefaultSyscalls::new(&store); let dummy_rand = ChainRand::new(TipsetKeys::new(vec![])); - let mut runtime = DefaultRuntime::new( + let mut runtime = DefaultRuntime::<_, _, DevnetParams>::new( &mut state, &store, &default_syscalls, @@ -106,8 +105,9 @@ fn transfer_test() { 0, 0, &dummy_rand, - ); - let _serialized = internal_send(&mut runtime, &message, 0).unwrap(); + ) + .unwrap(); + let _serialized = vm_send(&mut runtime, &message, None).unwrap(); let actor_state_result_1 = state.get_actor(&actor_addr_1).unwrap().unwrap(); let actor_state_result_2 = state.get_actor(&actor_addr_2).unwrap().unwrap(); diff --git a/vm/message/src/chain_message.rs b/vm/message/src/chain_message.rs index 4450f9cf36d8..a3a10b46e27a 100644 --- a/vm/message/src/chain_message.rs +++ b/vm/message/src/chain_message.rs @@ -66,13 +66,13 @@ impl Message for ChainMessage { Self::Unsigned(t) => t.set_gas_price(token_amount), } } - fn gas_limit(&self) -> u64 { + fn gas_limit(&self) -> i64 { match self { Self::Signed(t) => t.gas_limit(), Self::Unsigned(t) => t.gas_limit(), } } - fn set_gas_limit(&mut self, token_amount: u64) { + fn set_gas_limit(&mut self, token_amount: i64) { match self { Self::Signed(t) => t.set_gas_limit(token_amount), Self::Unsigned(t) => t.set_gas_limit(token_amount), diff --git a/vm/message/src/lib.rs b/vm/message/src/lib.rs index 6d1d57bdb56f..d9a1dad83c11 100644 --- a/vm/message/src/lib.rs +++ b/vm/message/src/lib.rs @@ -35,11 +35,11 @@ pub trait Message { /// sets the gas price fn set_gas_price(&mut self, amount: TokenAmount); /// sets the gas limit for the message - fn set_gas_limit(&mut self, _: u64); + fn set_gas_limit(&mut self, amount: i64); /// sets a new sequence to the message - fn set_sequence(&mut self, _: u64); + fn set_sequence(&mut self, sequence: u64); /// Returns the gas limit for the message - fn gas_limit(&self) -> u64; + fn gas_limit(&self) -> i64; /// Returns the required funds for the message fn required_funds(&self) -> TokenAmount; } diff --git a/vm/message/src/message_receipt.rs b/vm/message/src/message_receipt.rs index 6b35d21ba8e3..ab3db956da4b 100644 --- a/vm/message/src/message_receipt.rs +++ b/vm/message/src/message_receipt.rs @@ -9,7 +9,7 @@ use vm::{ExitCode, Serialized}; pub struct MessageReceipt { pub exit_code: ExitCode, pub return_data: Serialized, - pub gas_used: u64, + pub gas_used: i64, } #[cfg(feature = "json")] @@ -44,7 +44,7 @@ pub mod json { exit_code: u64, #[serde(rename = "Return")] return_data: &'a [u8], - gas_used: u64, + gas_used: i64, } MessageReceiptSer { exit_code: m.exit_code as u64, @@ -64,7 +64,7 @@ pub mod json { exit_code: u64, #[serde(rename = "Return")] return_data: Vec, - gas_used: u64, + gas_used: i64, } let MessageReceiptDe { exit_code, diff --git a/vm/message/src/signed_message.rs b/vm/message/src/signed_message.rs index 02c7e9163706..99d7588edbc2 100644 --- a/vm/message/src/signed_message.rs +++ b/vm/message/src/signed_message.rs @@ -88,10 +88,10 @@ impl Message for SignedMessage { fn set_gas_price(&mut self, token_amount: TokenAmount) { self.message.set_gas_price(token_amount) } - fn gas_limit(&self) -> u64 { + fn gas_limit(&self) -> i64 { self.message.gas_limit() } - fn set_gas_limit(&mut self, token_amount: u64) { + fn set_gas_limit(&mut self, token_amount: i64) { self.message.set_gas_limit(token_amount) } fn set_sequence(&mut self, new_sequence: u64) { diff --git a/vm/message/src/unsigned_message.rs b/vm/message/src/unsigned_message.rs index 8764975e035d..931ce17c2b73 100644 --- a/vm/message/src/unsigned_message.rs +++ b/vm/message/src/unsigned_message.rs @@ -56,7 +56,7 @@ pub struct UnsignedMessage { #[builder(default)] gas_price: TokenAmount, #[builder(default)] - gas_limit: u64, + gas_limit: i64, } impl UnsignedMessage { @@ -143,11 +143,10 @@ impl Message for UnsignedMessage { fn set_sequence(&mut self, new_sequence: u64) { self.sequence = new_sequence } - fn gas_limit(&self) -> u64 { + fn gas_limit(&self) -> i64 { self.gas_limit } - - fn set_gas_limit(&mut self, token_amount: u64) { + fn set_gas_limit(&mut self, token_amount: i64) { self.gas_limit = token_amount } fn required_funds(&self) -> TokenAmount { @@ -189,7 +188,7 @@ pub mod json { sequence: u64, value: String, gas_price: String, - gas_limit: u64, + gas_limit: i64, #[serde(rename = "Method")] method_num: u64, params: Option, diff --git a/vm/runtime/src/lib.rs b/vm/runtime/src/lib.rs index 655aa820fe27..bce62f9f5b17 100644 --- a/vm/runtime/src/lib.rs +++ b/vm/runtime/src/lib.rs @@ -42,11 +42,11 @@ pub trait Runtime { /// Validates the caller against some predicate. /// Exported actor methods must invoke at least one caller validation before returning. - fn validate_immediate_caller_accept_any(&self); - fn validate_immediate_caller_is<'a, I>(&self, addresses: I) -> Result<(), ActorError> + fn validate_immediate_caller_accept_any(&mut self) -> Result<(), ActorError>; + fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), ActorError> where I: IntoIterator; - fn validate_immediate_caller_type<'a, I>(&self, types: I) -> Result<(), ActorError> + fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError> where I: IntoIterator; @@ -56,10 +56,10 @@ pub trait Runtime { /// Resolves an address of any protocol to an ID address (via the Init actor's table). /// This allows resolution of externally-provided SECP, BLS, or actor addresses to the canonical form. /// If the argument is an ID address it is returned directly. - fn resolve_address(&self, address: &Address) -> Result; + fn resolve_address(&self, address: &Address) -> Result, ActorError>; /// Look up the code ID at an actor address. - fn get_actor_code_cid(&self, addr: &Address) -> Result; + fn get_actor_code_cid(&self, addr: &Address) -> Result, ActorError>; /// Randomness returns a (pseudo)random byte array drawing from a /// random beacon at a given epoch and incorporating reequisite entropy @@ -96,19 +96,19 @@ pub trait Runtime { fn store(&self) -> &BS; /// Sends a message to another actor, returning the exit code and return value envelope. - /// If the invoked method does not return successfully, its state changes (and that of any messages it sent in turn) - /// will be rolled back. + /// If the invoked method does not return successfully, its state changes + /// (and that of any messages it sent in turn) will be rolled back. fn send( &mut self, - to: &Address, + to: Address, method: MethodNum, - params: &Serialized, - value: &TokenAmount, + params: Serialized, + value: TokenAmount, ) -> Result; - /// Halts execution upon an error from which the receiver cannot recover. The caller will receive the exitcode and - /// an empty return value. State changes made within this call will be rolled back. - /// This method does not return. + /// Halts execution upon an error from which the receiver cannot recover. + /// The caller will receive the exitcode and an empty return value. + /// State changes made within this call will be rolled back. This method does not return. /// The message and args are for diagnostic purposes and do not persist on chain. fn abort>(&self, exit_code: ExitCode, msg: S) -> ActorError; @@ -118,7 +118,8 @@ pub trait Runtime { /// Always an ActorExec address. fn new_actor_address(&mut self) -> Result; - /// Creates an actor with code `codeID` and address `address`, with empty state. May only be called by Init actor. + /// Creates an actor with code `codeID` and address `address`, with empty state. + /// May only be called by Init actor. fn create_actor(&mut self, code_id: &Cid, address: &Address) -> Result<(), ActorError>; /// Deletes the executing actor from the state tree, transferring any balance to beneficiary. @@ -140,7 +141,8 @@ pub trait MessageInfo { /// The address of the actor receiving the message. Always an ID-address. fn receiver(&self) -> &Address; - /// The value attached to the message being processed, implicitly added to current_balance() before method invocation. + /// The value attached to the message being processed, implicitly + /// added to current_balance() before method invocation. fn value_received(&self) -> &TokenAmount; } diff --git a/vm/src/error.rs b/vm/src/error.rs index b31652fb9af1..0804b0324879 100644 --- a/vm/src/error.rs +++ b/vm/src/error.rs @@ -35,14 +35,22 @@ impl ActorError { } } + /// Returns true if error is fatal. pub fn is_fatal(&self) -> bool { self.fatal } + /// Returns the exit code of the error. pub fn exit_code(&self) -> ExitCode { self.exit_code } + /// Returns true when the exit code is `Ok`. + pub fn is_ok(&self) -> bool { + self.exit_code == ExitCode::Ok + } + + /// Error message of the actor error. pub fn msg(&self) -> &str { &self.msg } diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 513ad1038728..9566b38c5bbb 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -23,3 +23,30 @@ pub use self::invoc::*; pub use self::method::*; pub use self::randomness::*; pub use self::token::*; + +#[macro_use] +extern crate lazy_static; +use cid::{multihash::Blake2b256, Cid}; +use encoding::to_vec; + +lazy_static! { + /// Cbor bytes of an empty array serialized. + pub static ref EMPTY_ARR_BYTES: Vec = to_vec::<[(); 0]>(&[]).unwrap(); + + /// Cid of the empty array Cbor bytes (`EMPTY_ARR_BYTES`). + pub static ref EMPTY_ARR_CID: Cid = Cid::new_from_cbor(&EMPTY_ARR_BYTES, Blake2b256); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty_object_checks() { + assert_eq!(&*EMPTY_ARR_BYTES, &[128u8]); + assert_eq!( + EMPTY_ARR_CID.to_string(), + "bafy2bzacebc3bt6cedhoyw34drrmjvazhu4oj25er2ebk4u445pzycvq4ta4a" + ); + } +} diff --git a/vm/src/method.rs b/vm/src/method.rs index eff5ca8d576d..822698cab1e5 100644 --- a/vm/src/method.rs +++ b/vm/src/method.rs @@ -1,6 +1,7 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use super::EMPTY_ARR_BYTES; use encoding::{de, from_slice, ser, serde_bytes, to_vec, Cbor, Error as EncodingError}; use serde::{Deserialize, Serialize}; use std::ops::Deref; @@ -25,7 +26,9 @@ impl Default for Serialized { /// Default serialized bytes is an empty array serialized #[inline] fn default() -> Self { - Self::serialize::<[u8; 0]>([]).unwrap() + Self { + bytes: EMPTY_ARR_BYTES.clone(), + } } } @@ -44,6 +47,11 @@ impl Serialized { Self { bytes } } + /// Empty bytes constructor. Used for empty return values. + pub fn empty() -> Self { + Self { bytes: Vec::new() } + } + /// Contructor for encoding Cbor encodable structure pub fn serialize(obj: O) -> Result { Ok(Self { diff --git a/vm/state_tree/src/lib.rs b/vm/state_tree/src/lib.rs index d920835e1d94..70c03a29f829 100644 --- a/vm/state_tree/src/lib.rs +++ b/vm/state_tree/src/lib.rs @@ -49,7 +49,9 @@ where /// Get actor state from an address. Will be resolved to ID address. pub fn get_actor(&self, addr: &Address) -> Result, String> { - let addr = self.lookup_id(addr)?; + let addr = self + .lookup_id(addr)? + .ok_or_else(|| format!("Resolution lookup failed for {}", addr))?; // Check cache for actor state if let Some(actor_state) = self.actor_cache.read().get(&addr) { @@ -69,7 +71,9 @@ where /// Set actor state for an address. Will set state at ID address. pub fn set_actor(&mut self, addr: &Address, actor: ActorState) -> Result<(), String> { - let addr = self.lookup_id(addr)?; + let addr = self + .lookup_id(addr)? + .ok_or_else(|| format!("Resolution lookup failed for {}", addr))?; // Set actor state in cache if let Some(act) = self.actor_cache.write().insert(addr, actor.clone()) { @@ -88,9 +92,9 @@ where } /// Get an ID address from any Address - pub fn lookup_id(&self, addr: &Address) -> Result { + pub fn lookup_id(&self, addr: &Address) -> Result, String> { if addr.protocol() == Protocol::ID { - return Ok(*addr); + return Ok(Some(*addr)); } let init_act = self @@ -104,12 +108,14 @@ where .map_err(|e| e.to_string())? .ok_or("Could not resolve init actor state")?; - Ok(state.resolve_address(self.store(), addr)?) + state.resolve_address(self.store(), addr) } /// Delete actor for an address. Will resolve to ID address to delete. pub fn delete_actor(&mut self, addr: &Address) -> Result<(), String> { - let addr = self.lookup_id(addr)?; + let addr = self + .lookup_id(addr)? + .ok_or_else(|| format!("Resolution lookup failed for {}", addr))?; // Remove value from cache self.actor_cache.write().remove(&addr); @@ -138,11 +144,7 @@ where } /// Register a new address through the init actor. - pub fn register_new_address( - &mut self, - addr: &Address, - actor: ActorState, - ) -> Result { + pub fn register_new_address(&mut self, addr: &Address) -> Result { let mut init_act: ActorState = self .get_actor(&INIT_ACTOR_ADDR)? .ok_or("Could not retrieve init actor")?; @@ -168,9 +170,6 @@ where self.set_actor(&INIT_ACTOR_ADDR, init_act)?; - // After mutating the init actor, set the state at the ID address created - self.set_actor(&new_addr, actor)?; - Ok(new_addr) } diff --git a/vm/state_tree/tests/state_tree_tests.rs b/vm/state_tree/tests/state_tree_tests.rs index 1cf165bf6d6e..7c7d70de9411 100644 --- a/vm/state_tree/tests/state_tree_tests.rs +++ b/vm/state_tree/tests/state_tree_tests.rs @@ -88,16 +88,10 @@ fn get_set_non_id() { // Register new address let addr = Address::new_secp256k1(&[2; SECP_PUB_LEN]).unwrap(); - let secp_state = ActorState::new(e_cid.clone(), e_cid.clone(), Default::default(), 0); - let assigned_addr = tree - .register_new_address(&addr, secp_state.clone()) - .unwrap(); + let assigned_addr = tree.register_new_address(&addr).unwrap(); assert_eq!(assigned_addr, Address::new_id(100)); - // Test resolution of Secp address - assert_eq!(tree.get_actor(&addr).unwrap(), Some(secp_state)); - // Test reverting snapshot to before init actor set tree.revert_to_snapshot(&snapshot).unwrap(); assert_eq!(tree.snapshot().unwrap(), snapshot);