From 036e230cda30525b3087a2a84a4c16ff2a6c0e78 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 7 Apr 2022 13:28:39 +0200 Subject: [PATCH] fix: use custom errors for the runtime Fixes #196 --- actors/runtime/src/builtin/shared.rs | 3 +- actors/runtime/src/runtime/actor_code.rs | 3 +- actors/runtime/src/runtime/fvm.rs | 216 +++++++++++++---------- actors/runtime/src/runtime/mod.rs | 39 ++-- actors/runtime/src/test_utils.rs | 81 +++++---- actors/runtime/src/util/chaos/mod.rs | 19 +- 6 files changed, 216 insertions(+), 145 deletions(-) diff --git a/actors/runtime/src/builtin/shared.rs b/actors/runtime/src/builtin/shared.rs index 2ba233b789..23454b029c 100644 --- a/actors/runtime/src/builtin/shared.rs +++ b/actors/runtime/src/builtin/shared.rs @@ -6,6 +6,7 @@ use fvm_shared::address::Address; use fvm_shared::METHOD_SEND; use crate::runtime::Runtime; +use anyhow::Context; pub const HAMT_BIT_WIDTH: u32 = 5; @@ -24,7 +25,7 @@ where // send 0 balance to the account so an ID address for it is created and then try to resolve rt.send(*address, METHOD_SEND, Default::default(), Default::default()) - .map_err(|e| e.wrap(&format!("failed to send zero balance to address {}", address)))?; + .with_context(|| format!("failed to send zero balance to address {}", address))?; rt.resolve_address(address).ok_or_else(|| { anyhow::anyhow!( diff --git a/actors/runtime/src/runtime/actor_code.rs b/actors/runtime/src/runtime/actor_code.rs index b6895d560b..780bcadb54 100644 --- a/actors/runtime/src/runtime/actor_code.rs +++ b/actors/runtime/src/runtime/actor_code.rs @@ -21,5 +21,6 @@ pub trait ActorCode { // hold onto state between transactions. // https://github.com/filecoin-project/builtin-actors/issues/133 BS: Blockstore + Clone, - RT: Runtime; + RT: Runtime, + ActorError: From; } diff --git a/actors/runtime/src/runtime/fvm.rs b/actors/runtime/src/runtime/fvm.rs index dee547c265..c6e4a53bc6 100644 --- a/actors/runtime/src/runtime/fvm.rs +++ b/actors/runtime/src/runtime/fvm.rs @@ -1,6 +1,7 @@ -use anyhow::{anyhow, Error}; +use anyhow::anyhow; use cid::multihash::{Code, MultihashDigest}; use cid::Cid; +use fvm::error::NoStateError; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::{to_vec, Cbor, CborStore, RawBytes, DAG_CBOR}; use fvm_sdk as fvm; @@ -23,7 +24,9 @@ use fvm_shared::{ActorID, MethodNum}; use crate::runtime::actor_blockstore::ActorBlockstore; use crate::runtime::{ActorCode, ConsensusFault, MessageInfo, Policy, RuntimePolicy, Syscalls}; -use crate::{actor_error, ActorError, Runtime}; +use crate::Runtime; + +use super::RuntimeError; lazy_static! { /// Cid of the empty array Cbor bytes (`EMPTY_ARR_BYTES`). @@ -57,12 +60,9 @@ impl Default for FvmRuntime { } impl FvmRuntime { - fn assert_not_validated(&mut self) -> Result<(), ActorError> { + fn assert_not_validated(&mut self) -> Result<(), Error> { if self.caller_validated { - return Err(actor_error!( - SysErrIllegalActor, - "Method must validate caller identity exactly once" - )); + return Err(Error::CallerAlreadyValidated); } self.caller_validated = true; Ok(()) @@ -91,10 +91,60 @@ impl MessageInfo for FvmMessage { } } +#[derive(thiserror::Error, Debug)] +pub enum Error { + // SysErrIllegalActor + #[error("Method must validate caller identity exactly once")] + CallerAlreadyValidated, + // SysErrForbidden + #[error("caller {0} ist not one of supported")] + UnsupportedCallerAddress(Address), + // SysErrForbidden + #[error("caller cid type {0} not one of supported")] + UnsupportedCallerType(Cid), + #[error("syscall: {0}")] + Syscall(#[from] ErrorNumber), + // ErrIllegalState + #[error("failed to create state; expected empty array CID, got: {0}")] + RootNotEmpty(Cid), + #[error("encoding: {0}")] + CborStore(anyhow::Error), + #[error("no state: {0}")] + NoState(#[from] NoStateError), + #[error("exit code: {0}")] + NonZeroExitCode(ExitCode), + // SysErrIllegalActor + #[error("runtime.send() is not allowed")] + SendNotAllowed, + #[error("actor delete: {0}")] + ActorDelete(#[from] fvm::error::ActorDeleteError), +} + +impl RuntimeError for Error { + fn exit_code(&self) -> ExitCode { + use Error::*; + + match self { + CallerAlreadyValidated => ExitCode::SysErrIllegalActor, + UnsupportedCallerAddress(_) => ExitCode::SysErrForbidden, + UnsupportedCallerType(_) => ExitCode::SysErrForbidden, + Syscall(_e) => todo!(), // ? + RootNotEmpty(_) => ExitCode::ErrIllegalState, + CborStore(_) => ExitCode::ErrSerialization, //? + NoState(_) => ExitCode::ErrIllegalArgument, //? + NonZeroExitCode(e) => *e, + SendNotAllowed => ExitCode::ErrIllegalArgument, //? + ActorDelete(_) => ExitCode::ErrIllegalArgument, // ? + } + } +} + impl Runtime for FvmRuntime where B: Blockstore, { + type Error = Error; + fn network_version(&self) -> NetworkVersion { fvm::network::version() } @@ -107,11 +157,11 @@ where fvm::network::curr_epoch() } - fn validate_immediate_caller_accept_any(&mut self) -> Result<(), ActorError> { + fn validate_immediate_caller_accept_any(&mut self) -> Result<(), Self::Error> { self.assert_not_validated() } - fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), ActorError> + fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), Self::Error> where I: IntoIterator, { @@ -121,13 +171,11 @@ where if addresses.into_iter().any(|a| *a == caller_addr) { Ok(()) } else { - return Err(actor_error!(SysErrForbidden; - "caller {} is not one of supported", caller_addr - )); + return Err(Error::UnsupportedCallerAddress(caller_addr)); } } - fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError> + fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), Self::Error> where I: IntoIterator, { @@ -140,8 +188,7 @@ where match self.resolve_builtin_actor_type(&caller_cid) { Some(typ) if types.into_iter().any(|t| *t == typ) => Ok(()), - _ => Err(actor_error!(SysErrForbidden; - "caller cid type {} not one of supported", caller_cid)), + _ => Err(Error::UnsupportedCallerType(caller_cid)), } } @@ -170,19 +217,21 @@ where personalization: DomainSeparationTag, rand_epoch: ChainEpoch, entropy: &[u8], - ) -> Result { + ) -> Result { // Note: specs-actors treats all failures to get randomness as "fatal" errors, so we're free // to return whatever errors we see fit here. // // At the moment, we return "illegal argument" if the lookback is exceeded (not possible // with the current actors) and panic otherwise (as it indicates that we passed some // unexpected bad value to the syscall). - fvm::rand::get_chain_randomness(personalization, rand_epoch, entropy).map_err(|e| match e { - ErrorNumber::LimitExceeded => { - actor_error!(ErrIllegalArgument; "randomness lookback exceeded: {}", e) - } - e => panic!("get chain randomness failed with an unexpected error: {}", e), - }) + let res = fvm::rand::get_chain_randomness(personalization, rand_epoch, entropy)?; + Ok(res) + // .map_err(|e| match e { + // ErrorNumber::LimitExceeded => { + // actor_error!(ErrIllegalArgument; "randomness lookback exceeded: {}", e) + // } + // e => panic!("get chain randomness failed with an unexpected error: {}", e), + // }) } fn get_randomness_from_beacon( @@ -190,54 +239,50 @@ where personalization: DomainSeparationTag, rand_epoch: ChainEpoch, entropy: &[u8], - ) -> Result { + ) -> Result { // Note: specs-actors treats all failures to get randomness as "fatal" errors. See above. - fvm::rand::get_beacon_randomness(personalization, rand_epoch, entropy).map_err( - |e| match e { - ErrorNumber::LimitExceeded => { - actor_error!(ErrIllegalArgument; "randomness lookback exceeded: {}", e) - } - e => panic!("get chain randomness failed with an unexpected error: {}", e), - }, - ) - } - - fn create(&mut self, obj: &C) -> Result<(), ActorError> { + let res = fvm::rand::get_beacon_randomness(personalization, rand_epoch, entropy)?; + Ok(res) + // .map_err( + // |e| match e { + // ErrorNumber::LimitExceeded => { + // actor_error!(ErrIllegalArgument; "randomness lookback exceeded: {}", e) + // } + // e => panic!("get chain randomness failed with an unexpected error: {}", e), + // }, + // ) + } + + fn create(&mut self, obj: &C) -> Result<(), Self::Error> { let root = fvm::sself::root()?; if root != *EMPTY_ARR_CID { - return Err( - actor_error!(ErrIllegalState; "failed to create state; expected empty array CID, got: {}", root), - ); + return Err(Error::RootNotEmpty(root)); } - let new_root = ActorBlockstore.put_cbor(obj, Code::Blake2b256) - .map_err(|e| actor_error!(ErrIllegalArgument; "failed to write actor state during creation: {}", e.to_string()))?; + let new_root = ActorBlockstore.put_cbor(obj, Code::Blake2b256).map_err(Error::CborStore)?; fvm::sself::set_root(&new_root)?; Ok(()) } - fn state(&self) -> Result { + fn state(&self) -> Result { let root = fvm::sself::root()?; Ok(ActorBlockstore .get_cbor(&root) - .map_err( - |_| actor_error!(ErrIllegalArgument; "failed to get actor for Readonly state"), - )? + .map_err(Error::CborStore)? .expect("State does not exist for actor state root")) } - fn transaction(&mut self, f: F) -> Result + fn transaction(&mut self, f: F) -> Result where C: Cbor, - F: FnOnce(&mut C, &mut Self) -> Result, + F: FnOnce(&mut C, &mut Self) -> Result, { - let state_cid = fvm::sself::root() - .map_err(|_| actor_error!(ErrIllegalArgument; "failed to get actor root state CID"))?; + let state_cid = fvm::sself::root()?; log::debug!("getting cid: {}", state_cid); let mut state = ActorBlockstore .get_cbor::(&state_cid) - .map_err(|_| actor_error!(ErrIllegalArgument; "failed to get actor state"))? + .map_err(Error::CborStore)? .expect("State does not exist for actor state root"); self.in_transaction = true; @@ -245,8 +290,8 @@ where self.in_transaction = false; let ret = result?; - let new_root = ActorBlockstore.put_cbor(&state, Code::Blake2b256) - .map_err(|e| actor_error!(ErrIllegalArgument; "failed to write actor state in transaction: {}", e.to_string()))?; + let new_root = + ActorBlockstore.put_cbor(&state, Code::Blake2b256).map_err(Error::CborStore)?; fvm::sself::set_root(&new_root)?; Ok(ret) } @@ -261,51 +306,40 @@ where method: MethodNum, params: RawBytes, value: TokenAmount, - ) -> Result { + ) -> Result { if self.in_transaction { - return Err(actor_error!(SysErrIllegalActor; "runtime.send() is not allowed")); + return Err(Error::SendNotAllowed); } - // TODO: distinguish between "syscall" errors and actor exit codes. + match fvm::send::send(&to, method, params, value) { Ok(ret) => { if ret.exit_code.is_success() { Ok(ret.return_data) } else { - Err(ActorError::from(ret.exit_code)) + Err(Error::NonZeroExitCode(ret.exit_code)) } } - Err(err) => Err(match err { - ErrorNumber::NotFound => { - actor_error!(SysErrInvalidReceiver; "receiver not found") - } - ErrorNumber::InsufficientFunds => { - actor_error!(SysErrInsufficientFunds; "not enough funds") - } - ErrorNumber::LimitExceeded => { - actor_error!(SysErrForbidden; "recursion limit exceeded") - } + Err(err) => match err { + ErrorNumber::NotFound + | ErrorNumber::InsufficientFunds + | ErrorNumber::LimitExceeded => Err(Error::Syscall(err)), err => panic!("unexpected error: {}", err), - }), + }, } } - fn new_actor_address(&mut self) -> Result { + fn new_actor_address(&mut self) -> Result { Ok(fvm::actor::new_actor_address()) } - fn create_actor(&mut self, code_id: Cid, actor_id: ActorID) -> Result<(), ActorError> { - fvm::actor::create_actor(actor_id, &code_id).map_err(|e| { - ActorError::new( - match e { - ErrorNumber::IllegalArgument => ExitCode::SysErrIllegalArgument, - _ => panic!("create failed with unknown error: {}", e), - }, - "failed to create actor".into(), - ) + fn create_actor(&mut self, code_id: Cid, actor_id: ActorID) -> Result<(), Self::Error> { + fvm::actor::create_actor(actor_id, &code_id).map_err(|e| match e { + ErrorNumber::IllegalArgument => e.into(), + _ => panic!("create failed with unknown error: {}", e), }) } - fn delete_actor(&mut self, beneficiary: &Address) -> Result<(), ActorError> { + fn delete_actor(&mut self, beneficiary: &Address) -> Result<(), Self::Error> { Ok(fvm::sself::self_destruct(beneficiary)?) } @@ -331,10 +365,10 @@ where signature: &Signature, signer: &Address, plaintext: &[u8], - ) -> Result<(), Error> { + ) -> Result<(), anyhow::Error> { match fvm::crypto::verify_signature(signature, signer, plaintext) { Ok(true) => Ok(()), - Ok(false) | Err(_) => Err(Error::msg("invalid signature")), + Ok(false) | Err(_) => Err(anyhow::Error::msg("invalid signature")), } } @@ -346,24 +380,24 @@ where &self, proof_type: RegisteredSealProof, pieces: &[PieceInfo], - ) -> Result { + ) -> Result { // The only actor that invokes this (market actor) is generating the // exit code ErrIllegalArgument. We should probably move that here, or to the syscall itself. fvm::crypto::compute_unsealed_sector_cid(proof_type, pieces) .map_err(|e| anyhow!("failed to compute unsealed sector CID; exit code: {}", e)) } - fn verify_seal(&self, vi: &SealVerifyInfo) -> Result<(), Error> { + fn verify_seal(&self, vi: &SealVerifyInfo) -> Result<(), anyhow::Error> { match fvm::crypto::verify_seal(vi) { Ok(true) => Ok(()), - Ok(false) | Err(_) => Err(Error::msg("invalid seal")), + Ok(false) | Err(_) => Err(anyhow::Error::msg("invalid seal")), } } - fn verify_post(&self, verify_info: &WindowPoStVerifyInfo) -> Result<(), Error> { + fn verify_post(&self, verify_info: &WindowPoStVerifyInfo) -> Result<(), anyhow::Error> { match fvm::crypto::verify_post(verify_info) { Ok(true) => Ok(()), - Ok(false) | Err(_) => Err(Error::msg("invalid post")), + Ok(false) | Err(_) => Err(anyhow::Error::msg("invalid post")), } } @@ -372,28 +406,30 @@ where h1: &[u8], h2: &[u8], extra: &[u8], - ) -> Result, Error> { - fvm::crypto::verify_consensus_fault(h1, h2, extra).map_err(|_| Error::msg("no fault")) + ) -> Result, anyhow::Error> { + fvm::crypto::verify_consensus_fault(h1, h2, extra) + .map_err(|_| anyhow::Error::msg("no fault")) } fn batch_verify_seals(&self, batch: &[SealVerifyInfo]) -> anyhow::Result> { - fvm::crypto::batch_verify_seals(batch).map_err(|_| Error::msg("failed to verify batch")) + fvm::crypto::batch_verify_seals(batch) + .map_err(|_| anyhow::Error::msg("failed to verify batch")) } fn verify_aggregate_seals( &self, aggregate: &AggregateSealVerifyProofAndInfos, - ) -> Result<(), Error> { + ) -> Result<(), anyhow::Error> { match fvm::crypto::verify_aggregate_seals(aggregate) { Ok(true) => Ok(()), - Ok(false) | Err(_) => Err(Error::msg("invalid aggregate")), + Ok(false) | Err(_) => Err(anyhow::Error::msg("invalid aggregate")), } } - fn verify_replica_update(&self, replica: &ReplicaUpdateInfo) -> Result<(), Error> { + fn verify_replica_update(&self, replica: &ReplicaUpdateInfo) -> Result<(), anyhow::Error> { match fvm::crypto::verify_replica_update(replica) { Ok(true) => Ok(()), - Ok(false) | Err(_) => Err(Error::msg("invalid replica")), + Ok(false) | Err(_) => Err(anyhow::Error::msg("invalid replica")), } } } diff --git a/actors/runtime/src/runtime/mod.rs b/actors/runtime/src/runtime/mod.rs index bb84b66a7c..2cc8dbce27 100644 --- a/actors/runtime/src/runtime/mod.rs +++ b/actors/runtime/src/runtime/mod.rs @@ -11,6 +11,7 @@ use fvm_shared::consensus::ConsensusFault; use fvm_shared::crypto::randomness::DomainSeparationTag; use fvm_shared::crypto::signature::Signature; use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; use fvm_shared::piece::PieceInfo; use fvm_shared::randomness::Randomness; use fvm_shared::sector::{ @@ -35,9 +36,21 @@ mod actor_blockstore; mod policy; +pub trait RuntimeError: std::error::Error + Send + Sync + 'static { + fn exit_code(&self) -> ExitCode; +} + +impl From for ActorError { + fn from(e: E) -> Self { + ActorError::new(e.exit_code(), e.to_string()) + } +} + /// Runtime is the VM's internal runtime object. /// this is everything that is accessible to actors, beyond parameters. pub trait Runtime: Syscalls + RuntimePolicy { + type Error: RuntimeError; + /// The network protocol version number at the current epoch. fn network_version(&self) -> NetworkVersion; @@ -49,11 +62,11 @@ pub trait Runtime: Syscalls + RuntimePolicy { /// Validates the caller against some predicate. /// Exported actor methods must invoke at least one caller validation before returning. - fn validate_immediate_caller_accept_any(&mut self) -> Result<(), ActorError>; - fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), ActorError> + fn validate_immediate_caller_accept_any(&mut self) -> Result<(), Self::Error>; + fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), Self::Error> where I: IntoIterator; - fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError> + fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), Self::Error> where I: IntoIterator; @@ -76,7 +89,7 @@ pub trait Runtime: Syscalls + RuntimePolicy { personalization: DomainSeparationTag, rand_epoch: ChainEpoch, entropy: &[u8], - ) -> Result; + ) -> Result; /// Randomness returns a (pseudo)random byte array drawing from the latest /// beacon from a given epoch and incorporating requisite entropy. @@ -86,16 +99,16 @@ pub trait Runtime: Syscalls + RuntimePolicy { personalization: DomainSeparationTag, rand_epoch: ChainEpoch, entropy: &[u8], - ) -> Result; + ) -> Result; /// Initializes the state object. /// This is only valid in a constructor function and when the state has not yet been initialized. - fn create(&mut self, obj: &C) -> Result<(), ActorError>; + fn create(&mut self, obj: &C) -> Result<(), Self::Error>; /// Loads a readonly copy of the state of the receiver into the argument. /// /// Any modification to the state is illegal and will result in an abort. - fn state(&self) -> Result; + fn state(&self) -> Result; /// Loads a mutable version of the state into the `obj` argument and protects /// the execution from side effects (including message send). @@ -106,10 +119,10 @@ pub trait Runtime: Syscalls + RuntimePolicy { /// If the state is modified after this function returns, execution will abort. /// /// The gas cost of this method is that of a Store.Put of the mutated state object. - fn transaction(&mut self, f: F) -> Result + fn transaction(&mut self, f: F) -> Result where C: Cbor, - F: FnOnce(&mut C, &mut Self) -> Result; + F: FnOnce(&mut C, &mut Self) -> Result; /// Returns reference to blockstore fn store(&self) -> &BS; @@ -123,22 +136,22 @@ pub trait Runtime: Syscalls + RuntimePolicy { method: MethodNum, params: RawBytes, value: TokenAmount, - ) -> Result; + ) -> Result; /// Computes an address for a new actor. The returned address is intended to uniquely refer to /// the actor even in the event of a chain re-org (whereas an ID-address might refer to a /// different actor after messages are re-ordered). /// Always an ActorExec address. - fn new_actor_address(&mut self) -> Result; + 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. - fn create_actor(&mut self, code_id: Cid, address: ActorID) -> Result<(), ActorError>; + fn create_actor(&mut self, code_id: Cid, address: ActorID) -> Result<(), Self::Error>; /// Deletes the executing actor from the state tree, transferring any balance to beneficiary. /// Aborts if the beneficiary does not exist. /// May only be called by the actor itself. - fn delete_actor(&mut self, beneficiary: &Address) -> Result<(), ActorError>; + fn delete_actor(&mut self, beneficiary: &Address) -> Result<(), Self::Error>; /// Returns whether the specified CodeCID belongs to a built-in actor. fn resolve_builtin_actor_type(&self, code_id: &Cid) -> Option; diff --git a/actors/runtime/src/test_utils.rs b/actors/runtime/src/test_utils.rs index 0dc0735e18..216e315c5f 100644 --- a/actors/runtime/src/test_utils.rs +++ b/actors/runtime/src/test_utils.rs @@ -29,7 +29,9 @@ use fvm_shared::sector::{ use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, MethodNum}; -use crate::runtime::{ActorCode, MessageInfo, Policy, Runtime, RuntimePolicy, Syscalls}; +use crate::runtime::{ + ActorCode, MessageInfo, Policy, Runtime, RuntimeError, RuntimePolicy, Syscalls, +}; use crate::{actor_error, ActorError}; lazy_static! { @@ -362,7 +364,7 @@ impl MockRuntime { } #[allow(dead_code)] - pub fn get_state(&self) -> Result { + pub fn get_state(&self) -> Result { // TODO this doesn't handle errors exactly as go implementation self.state() } @@ -584,7 +586,19 @@ impl MessageInfo for MockRuntime { } } +#[derive(thiserror::Error, Debug)] +#[error("mock runtime error: {0}")] +pub struct MockRuntimeError(#[from] anyhow::Error); + +impl RuntimeError for MockRuntimeError { + fn exit_code(&self) -> ExitCode { + ExitCode::ErrIllegalArgument // TODO: what should this be? + } +} + impl Runtime for MockRuntime { + type Error = MockRuntimeError; + fn network_version(&self) -> NetworkVersion { self.network_version } @@ -599,7 +613,7 @@ impl Runtime for MockRuntime { self.epoch } - fn validate_immediate_caller_accept_any(&mut self) -> Result<(), ActorError> { + fn validate_immediate_caller_accept_any(&mut self) -> Result<(), Self::Error> { self.require_in_call(); assert!( self.expectations.borrow_mut().expect_validate_caller_any, @@ -609,7 +623,7 @@ impl Runtime for MockRuntime { Ok(()) } - fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), ActorError> + fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), Self::Error> where I: IntoIterator, { @@ -637,12 +651,15 @@ impl Runtime for MockRuntime { } } expectations.expect_validate_caller_addr = None; - return Err(actor_error!(SysErrForbidden; - "caller address {:?} forbidden, allowed: {:?}", - self.message().caller(), &addrs - )); + return Err(anyhow::anyhow!( + "caller address {:?} forbidden, allowed: {:?}", + self.message().caller(), + &addrs + ) + .into()); } - fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError> + + fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), Self::Error> where I: IntoIterator, { @@ -677,8 +694,8 @@ impl Runtime for MockRuntime { self.expectations.borrow_mut().expect_validate_caller_type = None; - Err(actor_error!(SysErrForbidden; "caller type {:?} forbidden, allowed: {:?}", - self.caller_type, types)) + Err(anyhow::anyhow!("caller type {:?} forbidden, allowed: {:?}", self.caller_type, types) + .into()) } fn current_balance(&self) -> TokenAmount { @@ -706,7 +723,7 @@ impl Runtime for MockRuntime { tag: DomainSeparationTag, epoch: ChainEpoch, entropy: &[u8], - ) -> Result { + ) -> Result { let expected = self .expectations .borrow_mut() @@ -739,7 +756,7 @@ impl Runtime for MockRuntime { tag: DomainSeparationTag, epoch: ChainEpoch, entropy: &[u8], - ) -> Result { + ) -> Result { let expected = self .expectations .borrow_mut() @@ -767,25 +784,25 @@ impl Runtime for MockRuntime { Ok(expected.out) } - fn create(&mut self, obj: &C) -> Result<(), ActorError> { + fn create(&mut self, obj: &C) -> Result<(), Self::Error> { if self.state.is_some() { - return Err(actor_error!(SysErrIllegalActor; "state already constructed")); + return Err(anyhow::anyhow!("state already constructed").into()); } self.state = Some(self.store.put_cbor(obj, Code::Blake2b256).unwrap()); Ok(()) } - fn state(&self) -> Result { + fn state(&self) -> Result { Ok(self.store.get_cbor(self.state.as_ref().unwrap()).unwrap().unwrap()) } - fn transaction(&mut self, f: F) -> Result + fn transaction(&mut self, f: F) -> Result where C: Cbor, - F: FnOnce(&mut C, &mut Self) -> Result, + F: FnOnce(&mut C, &mut Self) -> Result, { if self.in_transaction { - return Err(actor_error!(SysErrIllegalActor; "nested transaction")); + return Err(anyhow::anyhow!("nested transaction").into()); } let mut read_only = self.state()?; self.in_transaction = true; @@ -807,10 +824,10 @@ impl Runtime for MockRuntime { method: MethodNum, params: RawBytes, value: TokenAmount, - ) -> Result { + ) -> Result { self.require_in_call(); if self.in_transaction { - return Err(actor_error!(SysErrIllegalActor; "side-effect within transaction")); + return Err(anyhow::anyhow!("side-effect within transaction").into()); } assert!( @@ -831,31 +848,33 @@ impl Runtime for MockRuntime { { let mut balance = self.balance.borrow_mut(); if value > *balance { - return Err(actor_error!(SysErrSenderStateInvalid; - "cannot send value: {:?} exceeds balance: {:?}", - value, *balance - )); + return Err(anyhow::anyhow!( + "cannot send value: {:?} exceeds balance: {:?}", + value, + *balance + ) + .into()); } *balance -= value; } match expected_msg.exit_code { ExitCode::Ok => Ok(expected_msg.send_return), - x => Err(ActorError::new(x, "Expected message Fail".to_string())), + x => Err(anyhow::anyhow!("Expected message Fail: {}", x).into()), } } - fn new_actor_address(&mut self) -> Result { + fn new_actor_address(&mut self) -> Result { self.require_in_call(); let ret = *self.new_actor_addr.as_ref().expect("unexpected call to new actor address"); self.new_actor_addr = None; Ok(ret) } - fn create_actor(&mut self, code_id: Cid, actor_id: ActorID) -> Result<(), ActorError> { + fn create_actor(&mut self, code_id: Cid, actor_id: ActorID) -> Result<(), Self::Error> { self.require_in_call(); if self.in_transaction { - return Err(actor_error!(SysErrIllegalActor; "side-effect within transaction")); + return Err(anyhow::anyhow!("side-effect within transaction").into()); } let expect_create_actor = self .expectations @@ -868,10 +887,10 @@ impl Runtime for MockRuntime { Ok(()) } - fn delete_actor(&mut self, addr: &Address) -> Result<(), ActorError> { + fn delete_actor(&mut self, addr: &Address) -> Result<(), Self::Error> { self.require_in_call(); if self.in_transaction { - return Err(actor_error!(SysErrIllegalActor; "side-effect within transaction")); + return Err(anyhow::anyhow!("side-effect within transaction").into()); } let exp_act = self.expectations.borrow_mut().expect_delete_actor.take(); if exp_act.is_none() { diff --git a/actors/runtime/src/util/chaos/mod.rs b/actors/runtime/src/util/chaos/mod.rs index 2f274fb66e..ba70684b58 100644 --- a/actors/runtime/src/util/chaos/mod.rs +++ b/actors/runtime/src/util/chaos/mod.rs @@ -13,7 +13,7 @@ use num_traits::FromPrimitive; pub use state::*; pub use types::*; -use crate::runtime::{ActorCode, Runtime}; +use crate::runtime::{ActorCode, Runtime, RuntimeError}; use crate::{actor_error, cbor, ActorError}; mod state; @@ -49,7 +49,7 @@ pub enum Method { pub struct Actor; impl Actor { - pub fn send(rt: &mut RT, arg: SendArgs) -> Result + pub fn send(rt: &mut RT, arg: SendArgs) -> Result where BS: Blockstore, RT: Runtime, @@ -83,7 +83,7 @@ impl Actor { pub fn caller_validation( rt: &mut RT, args: CallerValidationArgs, - ) -> Result<(), ActorError> + ) -> Result<(), RT::Error> where BS: Blockstore, RT: Runtime, @@ -111,7 +111,7 @@ impl Actor { } // Creates an actor with the supplied CID and Address. - pub fn create_actor(rt: &mut RT, arg: CreateActorArgs) -> Result<(), ActorError> + pub fn create_actor(rt: &mut RT, arg: CreateActorArgs) -> Result<(), RT::Error> where BS: Blockstore, RT: Runtime, @@ -129,7 +129,7 @@ impl Actor { pub fn resolve_address( rt: &mut RT, args: Address, - ) -> Result + ) -> Result where BS: Blockstore, RT: Runtime, @@ -142,7 +142,7 @@ impl Actor { }) } - pub fn delete_actor(rt: &mut RT, beneficiary: Address) -> Result<(), ActorError> + pub fn delete_actor(rt: &mut RT, beneficiary: Address) -> Result<(), RT::Error> where BS: Blockstore, RT: Runtime, @@ -151,7 +151,7 @@ impl Actor { rt.delete_actor(&beneficiary) } - pub fn mutate_state(rt: &mut RT, arg: MutateStateArgs) -> Result<(), ActorError> + pub fn mutate_state(rt: &mut RT, arg: MutateStateArgs) -> Result<(), RT::Error> where BS: Blockstore, RT: Runtime, @@ -164,7 +164,7 @@ impl Actor { Ok(()) }), - _ => Err(actor_error!(ErrIllegalArgument; "Invalid mutate state command given" )), + _ => panic!("Invalid mutate state command given"), } } @@ -175,7 +175,7 @@ impl Actor { Err(ActorError::new(arg.code, arg.message)) } - pub fn inspect_runtime(rt: &mut RT) -> Result + pub fn inspect_runtime(rt: &mut RT) -> Result where BS: Blockstore, RT: Runtime, @@ -201,6 +201,7 @@ impl ActorCode for Actor { where BS: Blockstore, RT: Runtime, + ActorError: From, { match FromPrimitive::from_u64(method) { Some(Method::Constructor) => {