diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 144955bf1..63dab2732 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -4,11 +4,10 @@ use cid::Cid; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_error, cbor, ActorDowncast, ActorError, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{actor_error, cbor, ActorContext, ActorError, SYSTEM_ACTOR_ADDR}; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; -use fvm_shared::error::ExitCode; use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; @@ -42,10 +41,7 @@ impl Actor { { let sys_ref: &Address = &SYSTEM_ACTOR_ADDR; rt.validate_immediate_caller_is(std::iter::once(sys_ref))?; - let state = State::new(rt.store(), params.network_name).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct init actor state") - })?; - + let state = State::new(rt.store(), params.network_name)?; rt.create(&state)?; Ok(()) @@ -86,9 +82,8 @@ impl Actor { // Allocate an ID for this actor. // Store mapping of pubkey or actor address to actor ID let id_address: ActorID = rt.transaction(|s: &mut State, rt| { - s.map_address_to_new_id(rt.store(), &robust_address).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to allocate ID address") - }) + s.map_address_to_new_id(rt.store(), &robust_address) + .context("failed to allocate ID address") })?; // Create an empty actor @@ -101,7 +96,7 @@ impl Actor { params.constructor_params, rt.message().value_received(), ) - .map_err(|err| err.wrap("constructor failed"))?; + .context("constructor failed")?; Ok(ExecReturn { id_address: Address::new_id(id_address), robust_address }) } diff --git a/actors/init/src/state.rs b/actors/init/src/state.rs index d2157ec1e..5fac56320 100644 --- a/actors/init/src/state.rs +++ b/actors/init/src/state.rs @@ -1,15 +1,16 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use anyhow::anyhow; use cid::Cid; use fil_actors_runtime::{ - actor_error, make_empty_map, make_map_with_root_and_bitwidth, FIRST_NON_SINGLETON_ADDR, + actor_error, make_empty_map, make_map_with_root_and_bitwidth, ActorError, AsActorError, + FIRST_NON_SINGLETON_ADDR, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::Cbor; use fvm_shared::address::{Address, Protocol}; +use fvm_shared::error::ExitCode; use fvm_shared::{ActorID, HAMT_BIT_WIDTH}; /// State is reponsible for creating @@ -21,10 +22,10 @@ pub struct State { } impl State { - pub fn new(store: &BS, network_name: String) -> anyhow::Result { + pub fn new(store: &BS, network_name: String) -> Result { let empty_map = make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH) .flush() - .map_err(|e| anyhow!("failed to create empty map: {}", e))?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to create empty map")?; Ok(Self { address_map: empty_map, next_id: FIRST_NON_SINGLETON_ADDR, network_name }) } @@ -36,22 +37,26 @@ impl State { &mut self, store: &BS, addr: &Address, - ) -> anyhow::Result { + ) -> Result { let id = self.next_id; self.next_id += 1; - let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH)?; - let is_new = map.set_if_absent(addr.to_bytes().into(), id)?; + let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load address map")?; + let is_new = map + .set_if_absent(addr.to_bytes().into(), id) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set map key")?; if !is_new { // this is impossible today as the robust address is a hash of unique inputs // but in close future predictable address generation will make this possible - return Err(anyhow!(actor_error!( + return Err(actor_error!( forbidden, "robust address {} is already allocated in the address map", addr - ))); + )); } - self.address_map = map.flush()?; + self.address_map = + map.flush().context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store address map")?; Ok(id) } @@ -70,14 +75,18 @@ impl State { &self, store: &BS, addr: &Address, - ) -> anyhow::Result> { + ) -> Result, ActorError> { if addr.protocol() == Protocol::ID { return Ok(Some(*addr)); } - let map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH)?; + let map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load address map")?; - Ok(map.get(&addr.to_bytes())?.copied().map(Address::new_id)) + let found = map + .get(&addr.to_bytes()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get address entry")?; + Ok(found.copied().map(Address::new_id)) } } diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index b9749ebe0..308c2a67b 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -6,8 +6,8 @@ use std::collections::BTreeSet; use fil_actors_runtime::cbor::serialize_vec; use fil_actors_runtime::runtime::{ActorCode, Primitives, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, ActorDowncast, - ActorError, Map, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, + actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, ActorContext, + ActorError, AsActorError, Map, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; @@ -29,8 +29,6 @@ mod state; pub mod testing; mod types; -// * Updated to specs-actors commit: 845089a6d2580e46055c24415a6c32ee688e5186 (v3.0.0) - /// Multisig actor methods available #[derive(FromPrimitive)] #[repr(u64)] @@ -73,12 +71,7 @@ impl Actor { let mut resolved_signers = Vec::with_capacity(params.signers.len()); let mut dedup_signers = BTreeSet::new(); for signer in ¶ms.signers { - let resolved = resolve_to_actor_id(rt, signer).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", signer), - ) - })?; + let resolved = resolve_to_actor_id(rt, signer)?; if !dedup_signers.insert(resolved) { return Err( actor_error!(illegal_argument; "duplicate signer not allowed: {}", signer), @@ -101,10 +94,9 @@ impl Actor { return Err(actor_error!(illegal_argument; "negative unlock duration disallowed")); } - let empty_root = - make_empty_map::<_, ()>(rt.store(), HAMT_BIT_WIDTH).flush().map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "Failed to create empty map") - })?; + let empty_root = make_empty_map::<_, ()>(rt.store(), HAMT_BIT_WIDTH) + .flush() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to create empty map")?; let mut st: State = State { signers: resolved_signers, @@ -150,12 +142,8 @@ impl Actor { return Err(actor_error!(forbidden, "{} is not a signer", proposer)); } - let mut ptx = make_map_with_root(&st.pending_txs, rt.store()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load pending transactions", - ) - })?; + let mut ptx = make_map_with_root(&st.pending_txs, rt.store()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending transactions")?; let t_id = st.next_tx_id; st.next_tx_id.0 += 1; @@ -168,19 +156,15 @@ impl Actor { approved: Vec::new(), }; - ptx.set(t_id.key(), txn.clone()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to put transaction for propose", - ) - })?; + ptx.set(t_id.key(), txn.clone()).context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to put transaction for propose", + )?; - st.pending_txs = ptx.flush().map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to flush pending transactions", - ) - })?; + st.pending_txs = ptx.flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to flush pending transactions", + )?; Ok((t_id, txn)) })?; @@ -205,12 +189,8 @@ impl Actor { return Err(actor_error!(forbidden; "{} is not a signer", approver)); } - let ptx = make_map_with_root(&st.pending_txs, rt.store()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load pending transactions", - ) - })?; + let ptx = make_map_with_root(&st.pending_txs, rt.store()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending transactions")?; let txn = get_transaction(rt, &ptx, params.id, params.proposal_hash)?; @@ -245,20 +225,12 @@ impl Actor { } let mut ptx = make_map_with_root::<_, Transaction>(&st.pending_txs, rt.store()) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load pending transactions", - ) - })?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending transactions")?; let (_, tx) = ptx .delete(¶ms.id.key()) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to pop transaction {:?} for cancel", params.id), - ) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to pop transaction {:?} for cancel", params.id) })? .ok_or_else(|| { actor_error!(not_found, "no such transaction {:?} to cancel", params.id) @@ -269,23 +241,19 @@ impl Actor { return Err(actor_error!(forbidden; "Cannot cancel another signers transaction")); } - let calculated_hash = compute_proposal_hash(&tx, rt).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to compute proposal hash for (tx: {:?})", params.id), - ) - })?; + let calculated_hash = compute_proposal_hash(&tx, rt) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to compute proposal hash for (tx: {:?})", params.id) + })?; if !params.proposal_hash.is_empty() && params.proposal_hash != calculated_hash { return Err(actor_error!(illegal_state, "hash does not match proposal params")); } - st.pending_txs = ptx.flush().map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to flush pending transactions", - ) - })?; + st.pending_txs = ptx.flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to flush pending transactions", + )?; Ok(()) }) @@ -299,12 +267,7 @@ impl Actor { { let receiver = rt.message().receiver(); rt.validate_immediate_caller_is(std::iter::once(&receiver))?; - let resolved_new_signer = resolve_to_actor_id(rt, ¶ms.signer).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {} to ID", params.signer), - ) - })?; + let resolved_new_signer = resolve_to_actor_id(rt, ¶ms.signer)?; rt.transaction(|st: &mut State, _| { if st.signers.len() >= SIGNERS_MAX { @@ -336,12 +299,7 @@ impl Actor { { let receiver = rt.message().receiver(); rt.validate_immediate_caller_is(std::iter::once(&receiver))?; - let resolved_old_signer = resolve_to_actor_id(rt, ¶ms.signer).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {} to ID", params.signer), - ) - })?; + let resolved_old_signer = resolve_to_actor_id(rt, ¶ms.signer)?; rt.transaction(|st: &mut State, rt| { if !st.is_signer(&Address::new_id(resolved_old_signer)) { @@ -374,12 +332,8 @@ impl Actor { } // Remove approvals from removed signer - st.purge_approvals(rt.store(), &Address::new_id(resolved_old_signer)).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to purge approvals of removed signer", - ) - })?; + st.purge_approvals(rt.store(), &Address::new_id(resolved_old_signer)) + .context("failed to purge approvals of removed signer")?; st.signers.retain(|s| s != &Address::new_id(resolved_old_signer)); Ok(()) @@ -396,18 +350,8 @@ impl Actor { { let receiver = rt.message().receiver(); rt.validate_immediate_caller_is(std::iter::once(&receiver))?; - let from_resolved = resolve_to_actor_id(rt, ¶ms.from).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {} to ID", params.from), - ) - })?; - let to_resolved = resolve_to_actor_id(rt, ¶ms.to).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {} to ID", params.to), - ) - })?; + let from_resolved = resolve_to_actor_id(rt, ¶ms.from)?; + let to_resolved = resolve_to_actor_id(rt, ¶ms.to)?; rt.transaction(|st: &mut State, rt| { if !st.is_signer(&Address::new_id(from_resolved)) { @@ -424,12 +368,7 @@ impl Actor { // Add new signer st.signers.push(Address::new_id(to_resolved)); - st.purge_approvals(rt.store(), &Address::new_id(from_resolved)).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to purge approvals of removed signer", - ) - })?; + st.purge_approvals(rt.store(), &Address::new_id(from_resolved))?; Ok(()) })?; @@ -510,29 +449,21 @@ impl Actor { } let st = rt.transaction(|st: &mut State, rt| { - let mut ptx = make_map_with_root(&st.pending_txs, rt.store()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load pending transactions", - ) - })?; + let mut ptx = make_map_with_root(&st.pending_txs, rt.store()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending transactions")?; // update approved on the transaction txn.approved.push(rt.message().caller()); - ptx.set(tx_id.key(), txn.clone()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to put transaction {} for approval", tx_id.0), - ) - })?; + ptx.set(tx_id.key(), txn.clone()) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to put transaction {} for approval", tx_id.0) + })?; - st.pending_txs = ptx.flush().map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to flush pending transactions", - ) - })?; + st.pending_txs = ptx.flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to flush pending transactions", + )?; // Go implementation holds reference to state after transaction so this must be cloned // to match to handle possible exit code inconsistency @@ -558,8 +489,7 @@ where let mut applied = false; let threshold_met = txn.approved.len() as u64 >= st.num_approvals_threshold; if threshold_met { - st.check_available(rt.current_balance(), &txn.value, rt.curr_epoch()) - .map_err(|e| actor_error!(insufficient_funds, "insufficient funds unlocked: {}", e))?; + st.check_available(rt.current_balance(), &txn.value, rt.curr_epoch())?; match rt.send(&txn.to, txn.method, txn.params.clone(), txn.value.clone()) { Ok(ser) => { @@ -573,26 +503,17 @@ where rt.transaction(|st: &mut State, rt| { let mut ptx = make_map_with_root::<_, Transaction>(&st.pending_txs, rt.store()) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load pending transactions", - ) - })?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending transactions")?; - ptx.delete(&txn_id.key()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to delete transaction for cleanup", - ) - })?; + ptx.delete(&txn_id.key()).context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to delete transaction for cleanup", + )?; - st.pending_txs = ptx.flush().map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to flush pending transactions", - ) - })?; + st.pending_txs = ptx.flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to flush pending transactions", + )?; Ok(()) })?; } @@ -612,21 +533,16 @@ where { let txn = ptx .get(&txn_id.key()) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load transaction {:?} for approval", txn_id), - ) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to load transaction {:?} for approval", txn_id) })? .ok_or_else(|| actor_error!(not_found, "no such transaction {:?} for approval", txn_id))?; if !proposal_hash.is_empty() { - let calculated_hash = compute_proposal_hash(txn, rt).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to compute proposal hash for (tx: {:?})", txn_id), - ) - })?; + let calculated_hash = compute_proposal_hash(txn, rt) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to compute proposal hash for (tx: {:?})", txn_id) + })?; if proposal_hash != calculated_hash { return Err(actor_error!( diff --git a/actors/multisig/src/state.rs b/actors/multisig/src/state.rs index 67d387503..d77af3f12 100644 --- a/actors/multisig/src/state.rs +++ b/actors/multisig/src/state.rs @@ -1,8 +1,8 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use anyhow::anyhow; use cid::Cid; +use fil_actors_runtime::{actor_error, ActorError, AsActorError}; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::Cbor; @@ -11,6 +11,7 @@ use fvm_shared::bigint::BigInt; use fvm_shared::bigint::Integer; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; use indexmap::IndexMap; use num_traits::Zero; @@ -75,8 +76,9 @@ impl State { &mut self, store: &BS, addr: &Address, - ) -> anyhow::Result<()> { - let mut txns = make_map_with_root(&self.pending_txs, store)?; + ) -> Result<(), ActorError> { + let mut txns = make_map_with_root(&self.pending_txs, store) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load txn map")?; // Identify transactions that need updating let mut txn_ids_to_purge = IndexMap::new(); @@ -87,20 +89,24 @@ impl State { } } Ok(()) - })?; + }) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to scan txns")?; // Update or remove those transactions. for (tx_id, mut txn) in txn_ids_to_purge { txn.approved.retain(|approver| approver != addr); if !txn.approved.is_empty() { - txns.set(tx_id.into(), txn)?; + txns.set(tx_id.into(), txn) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to update entry")?; } else { - txns.delete(&tx_id)?; + txns.delete(&tx_id) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to delete entry")?; } } - self.pending_txs = txns.flush()?; + self.pending_txs = + txns.flush().context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store entries")?; Ok(()) } @@ -110,12 +116,17 @@ impl State { balance: TokenAmount, amount_to_spend: &TokenAmount, curr_epoch: ChainEpoch, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { if amount_to_spend.is_negative() { - return Err(anyhow!("amount to spend {} less than zero", amount_to_spend)); + return Err(actor_error!( + illegal_argument, + "amount to spend {} less than zero", + amount_to_spend + )); } if &balance < amount_to_spend { - return Err(anyhow!( + return Err(actor_error!( + insufficient_funds, "current balance {} less than amount to spend {}", balance, amount_to_spend @@ -131,7 +142,8 @@ impl State { let remaining_balance = balance - amount_to_spend; let amount_locked = self.amount_locked(curr_epoch - self.start_epoch); if remaining_balance < amount_locked { - return Err(anyhow!( + return Err(actor_error!( + insufficient_funds, "actor balance {} if spent {} would be less than required locked amount {}", remaining_balance, amount_to_spend, diff --git a/actors/multisig/tests/multisig_actor_test.rs b/actors/multisig/tests/multisig_actor_test.rs index 9a6d00c57..a31d451b5 100644 --- a/actors/multisig/tests/multisig_actor_test.rs +++ b/actors/multisig/tests/multisig_actor_test.rs @@ -227,7 +227,7 @@ mod constructor_tests { ); rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); expect_abort( - ExitCode::USR_ILLEGAL_STATE, + ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( Method::Constructor as u64, &RawBytes::serialize(¶ms).unwrap(), diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index cd16267fd..226030a1f 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -75,12 +75,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - let resolved = resolve_to_actor_id(rt, raw).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {} to ID", raw), - ) - })?; + let resolved = resolve_to_actor_id(rt, raw)?; let code_cid = rt .get_actor_code_cid(&resolved) diff --git a/actors/paych/tests/paych_actor_test.rs b/actors/paych/tests/paych_actor_test.rs index 769c6ec8b..5a010309e 100644 --- a/actors/paych/tests/paych_actor_test.rs +++ b/actors/paych/tests/paych_actor_test.rs @@ -230,7 +230,7 @@ mod paych_constructor { &mut rt, METHOD_CONSTRUCTOR, &RawBytes::serialize(¶ms).unwrap(), - ExitCode::USR_ILLEGAL_STATE, + ExitCode::USR_ILLEGAL_ARGUMENT, ); } @@ -267,7 +267,7 @@ mod paych_constructor { &mut rt, METHOD_CONSTRUCTOR, &RawBytes::serialize(¶ms).unwrap(), - ExitCode::USR_ILLEGAL_STATE, + ExitCode::USR_ILLEGAL_ARGUMENT, ); } } diff --git a/actors/system/src/lib.rs b/actors/system/src/lib.rs index d8b8c2c5b..1c6ef5f38 100644 --- a/actors/system/src/lib.rs +++ b/actors/system/src/lib.rs @@ -1,6 +1,5 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use anyhow::anyhow; use cid::{multihash, Cid}; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; @@ -12,13 +11,11 @@ use num_derive::FromPrimitive; use num_traits::FromPrimitive; use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_error, ActorDowncast, ActorError, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{actor_error, ActorContext, ActorError, AsActorError, SYSTEM_ACTOR_ADDR}; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(Actor); -// * Updated to specs-actors commit: 845089a6d2580e46055c24415a6c32ee688e5186 (v3.0.0) - /// System actor methods. #[derive(FromPrimitive)] #[repr(u64)] @@ -35,10 +32,10 @@ pub struct State { impl Cbor for State {} impl State { - pub fn new(store: &BS) -> anyhow::Result { + pub fn new(store: &BS) -> Result { let c = store .put_cbor(&Vec::<(String, Cid)>::new(), multihash::Code::Blake2b256) - .map_err(|e| anyhow!("failed to put system state to store: {}", e))?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store system state")?; Ok(Self { builtin_actors: c }) } @@ -65,9 +62,7 @@ impl Actor { { rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?; - let state = State::new(rt.store()).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct state") - })?; + let state = State::new(rt.store()).context("failed to construct state")?; rt.create(&state)?; Ok(()) } diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index 3db2e19da..8d91e76db 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -79,12 +79,7 @@ impl Actor { )); } - let verifier = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", params.address), - ) - })?; + let verifier = resolve_to_actor_id(rt, ¶ms.address)?; let verifier = Address::new_id(verifier); @@ -145,13 +140,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - let verifier = resolve_to_actor_id(rt, &verifier_addr).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", verifier_addr), - ) - })?; - + let verifier = resolve_to_actor_id(rt, &verifier_addr)?; let verifier = Address::new_id(verifier); let state: State = rt.state()?; @@ -204,13 +193,7 @@ impl Actor { )); } - let client = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", params.address), - ) - })?; - + let client = resolve_to_actor_id(rt, ¶ms.address)?; let client = Address::new_id(client); let st: State = rt.state()?; @@ -327,13 +310,7 @@ impl Actor { { rt.validate_immediate_caller_is(std::iter::once(&*STORAGE_MARKET_ACTOR_ADDR))?; - let client = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", params.address), - ) - })?; - + let client = resolve_to_actor_id(rt, ¶ms.address)?; let client = Address::new_id(client); if params.deal_size < rt.policy().minimum_verified_deal_size { @@ -437,13 +414,7 @@ impl Actor { )); } - let client = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", params.address), - ) - })?; - + let client = resolve_to_actor_id(rt, ¶ms.address)?; let client = Address::new_id(client); let st: State = rt.state()?; @@ -520,39 +491,13 @@ impl Actor { BS: Blockstore, RT: Runtime, { - let client = resolve_to_actor_id(rt, ¶ms.verified_client_to_remove).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_ARGUMENT, - format!("failed to resolve client addr {} to ID", params.verified_client_to_remove), - ) - })?; - + let client = resolve_to_actor_id(rt, ¶ms.verified_client_to_remove)?; let client = Address::new_id(client); - let verifier_1 = - resolve_to_actor_id(rt, ¶ms.verifier_request_1.verifier).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_ARGUMENT, - format!( - "failed to resolve verifier addr {} to ID", - params.verifier_request_1.verifier - ), - ) - })?; - + let verifier_1 = resolve_to_actor_id(rt, ¶ms.verifier_request_1.verifier)?; let verifier_1 = Address::new_id(verifier_1); - let verifier_2 = - resolve_to_actor_id(rt, ¶ms.verifier_request_2.verifier).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_ARGUMENT, - format!( - "failed to resolve verifier addr {} to ID", - params.verifier_request_2.verifier - ), - ) - })?; - + let verifier_2 = resolve_to_actor_id(rt, ¶ms.verifier_request_2.verifier)?; let verifier_2 = Address::new_id(verifier_2); if verifier_1 == verifier_2 { diff --git a/actors/verifreg/tests/verifreg_actor_test.rs b/actors/verifreg/tests/verifreg_actor_test.rs index c3be28044..e5e67faa7 100644 --- a/actors/verifreg/tests/verifreg_actor_test.rs +++ b/actors/verifreg/tests/verifreg_actor_test.rs @@ -150,7 +150,7 @@ mod verifiers { ExitCode::OK, ); expect_abort( - ExitCode::USR_ILLEGAL_STATE, + ExitCode::USR_ILLEGAL_ARGUMENT, h.add_verifier(&mut rt, &verifier_key_address, &allowance), ); h.check_state(&rt); @@ -335,7 +335,7 @@ mod clients { ); expect_abort( - ExitCode::USR_ILLEGAL_STATE, + ExitCode::USR_ILLEGAL_ARGUMENT, h.add_client(&mut rt, &VERIFIER, &client, &allowance_client, &allowance_client), ); h.check_state(&rt); diff --git a/runtime/src/actor_error.rs b/runtime/src/actor_error.rs index 899d63956..8ef17f147 100644 --- a/runtime/src/actor_error.rs +++ b/runtime/src/actor_error.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use fvm_shared::error::ExitCode; use thiserror::Error; @@ -108,3 +110,95 @@ macro_rules! actor_error { $crate::actor_error!($code; $msg $(, $ex)*) }; } + +// Adds context to an actor error's descriptive message. +pub trait ActorContext { + fn context(self, context: C) -> Result + where + C: Display + 'static; + + fn with_context(self, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C; +} + +impl ActorContext for Result { + fn context(self, context: C) -> Result + where + C: Display + 'static, + { + self.map_err(|mut err| { + err.msg = format!("{}: {}", context, err.msg); + err + }) + } + + fn with_context(self, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C, + { + self.map_err(|mut err| { + err.msg = format!("{}: {}", f(), err.msg); + err + }) + } +} + +// Adapts a target into an actor error. +pub trait AsActorError: Sized { + fn exit_code(self, code: ExitCode) -> Result; + + fn context_code(self, code: ExitCode, context: C) -> Result + where + C: Display + 'static; + + fn with_context_code(self, code: ExitCode, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C; +} + +// Note: E should be std::error::Error, revert to this after anyhow:Error is no longer used. +impl AsActorError for Result { + fn exit_code(self, code: ExitCode) -> Result { + self.map_err(|err| ActorError { exit_code: code, msg: err.to_string() }) + } + + fn context_code(self, code: ExitCode, context: C) -> Result + where + C: Display + 'static, + { + self.map_err(|err| ActorError { exit_code: code, msg: format!("{}: {}", context, err) }) + } + + fn with_context_code(self, code: ExitCode, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C, + { + self.map_err(|err| ActorError { exit_code: code, msg: format!("{}: {}", f(), err) }) + } +} + +impl AsActorError for Option { + fn exit_code(self, code: ExitCode) -> Result { + self.ok_or_else(|| ActorError { exit_code: code, msg: "None".to_string() }) + } + + fn context_code(self, code: ExitCode, context: C) -> Result + where + C: Display + 'static, + { + self.ok_or_else(|| ActorError { exit_code: code, msg: context.to_string() }) + } + + fn with_context_code(self, code: ExitCode, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C, + { + self.ok_or_else(|| ActorError { exit_code: code, msg: f().to_string() }) + } +} diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index 4b5b0d14e..04555d20b 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -1,7 +1,7 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use anyhow::Ok; +use crate::{actor_error, ActorContext, ActorError}; use fvm_ipld_blockstore::Blockstore; use fvm_shared::address::Address; use fvm_shared::ActorID; @@ -20,7 +20,7 @@ pub const CALLER_TYPES_SIGNABLE: &[Type] = &[Type::Account, Type::Multisig]; /// ResolveToActorID resolves the given address to it's actor ID. /// If an actor ID for the given address dosen't exist yet, it tries to create one by sending /// a zero balance to the given address. -pub fn resolve_to_actor_id(rt: &mut RT, address: &Address) -> anyhow::Result +pub fn resolve_to_actor_id(rt: &mut RT, address: &Address) -> Result where BS: Blockstore, RT: Runtime, @@ -32,14 +32,11 @@ 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))?; if let Some(id) = rt.resolve_address(address) { return Ok(id); } - Err(anyhow::anyhow!( - "failed to resolve address {} to ID even after sending zero balance", - address, - )) + Err(actor_error!(illegal_argument, "failed to resolve or initialize address {}", address)) }