diff --git a/core/src/contract_container.rs b/core/src/contract_container.rs index 4b47ac85..a0a6d1bb 100644 --- a/core/src/contract_container.rs +++ b/core/src/contract_container.rs @@ -1,5 +1,5 @@ use crate::entry_point_callback::{Argument, EntryPointsCaller}; -use crate::{prelude::*, OdraResult}; +use crate::{prelude::*, ExecutionError, OdraResult}; use crate::{CallDef, OdraError, VmError}; use casper_types::bytesrepr::Bytes; use casper_types::RuntimeArgs; @@ -53,7 +53,7 @@ impl ContractContainer { })); } } else { - return Err(OdraError::VmError(VmError::MissingArg)); + return Err(OdraError::ExecutionError(ExecutionError::MissingArg)); } } Ok(()) @@ -72,7 +72,7 @@ mod tests { casper_types::{runtime_args, RuntimeArgs}, OdraError, VmError }; - use crate::{prelude::*, CallDef, ContractEnv}; + use crate::{prelude::*, CallDef, ContractEnv, ExecutionError}; const TEST_ENTRYPOINT: &str = "ep"; @@ -112,7 +112,10 @@ mod tests { let result = instance.call(call_def); // Then MissingArg error is returned. - assert_eq!(result.unwrap_err(), OdraError::VmError(VmError::MissingArg)); + assert_eq!( + result.unwrap_err(), + OdraError::ExecutionError(ExecutionError::MissingArg) + ); } #[test] @@ -144,7 +147,10 @@ mod tests { let result = instance.call(call_def); // Then MissingArg error is returned. - assert_eq!(result.unwrap_err(), OdraError::VmError(VmError::MissingArg)); + assert_eq!( + result.unwrap_err(), + OdraError::ExecutionError(ExecutionError::MissingArg) + ); } #[test] @@ -157,7 +163,10 @@ mod tests { let result = instance.call(call_def); // Then MissingArg error is returned. - assert_eq!(result.unwrap_err(), OdraError::VmError(VmError::MissingArg)); + assert_eq!( + result.unwrap_err(), + OdraError::ExecutionError(ExecutionError::MissingArg) + ); } impl ContractContainer { diff --git a/core/src/contract_context.rs b/core/src/contract_context.rs index d421b3d1..f1b7166d 100644 --- a/core/src/contract_context.rs +++ b/core/src/contract_context.rs @@ -3,7 +3,7 @@ use casper_types::CLValue; use crate::call_def::CallDef; use crate::casper_types::bytesrepr::Bytes; use crate::casper_types::U512; -use crate::{Address, OdraError}; +use crate::{Address, OdraError, OdraResult}; /// Trait representing the context of a smart contract. #[cfg_attr(test, allow(unreachable_code))] @@ -133,7 +133,7 @@ pub trait ContractContext { /// # Returns /// /// The value of the named argument as a byte array. - fn get_named_arg_bytes(&self, name: &str) -> Bytes; + fn get_named_arg_bytes(&self, name: &str) -> OdraResult; /// Similar to `get_named_arg_bytes`, but returns `None` if the named argument is not present. fn get_opt_named_arg_bytes(&self, name: &str) -> Option; diff --git a/core/src/contract_env.rs b/core/src/contract_env.rs index 22c12234..cd1178d8 100644 --- a/core/src/contract_env.rs +++ b/core/src/contract_env.rs @@ -287,8 +287,11 @@ impl ExecutionEnv { /// the contract will revert. pub fn get_named_arg(&self, name: &str) -> T { if T::is_required() { - let bytes = self.env.backend.borrow().get_named_arg_bytes(name); - deserialize_from_slice(bytes).unwrap_or_revert(&self.env) + let result = self.env.backend.borrow().get_named_arg_bytes(name); + match result { + Ok(bytes) => deserialize_from_slice(bytes).unwrap_or_revert(&self.env), + Err(err) => self.env.revert(err) + } } else { let bytes = self.env.backend.borrow().get_opt_named_arg_bytes(name); let result = diff --git a/core/src/error.rs b/core/src/error.rs index e810e948..e7511b57 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -125,6 +125,8 @@ pub enum ExecutionError { CouldNotSignMessage = 120, /// Empty dictionary name EmptyDictionaryName = 121, + /// Calling a contract with missing entrypoint arguments. + MissingArg = 122, /// Maximum code for user errors MaxUserError = 32767, /// User error too high. The code should be in range 0..32767. @@ -167,8 +169,6 @@ pub enum VmError { InvalidContractAddress, /// Error calling a host function in a wrong context. InvalidContext, - /// Calling a contract with missing entrypoint arguments. - MissingArg, /// Calling a contract with a wrong argument type. TypeMismatch { /// Expected type. diff --git a/core/src/host.rs b/core/src/host.rs index acc85bda..e1047c66 100644 --- a/core/src/host.rs +++ b/core/src/host.rs @@ -5,7 +5,7 @@ use crate::{ call_result::CallResult, contract_def::HasIdent, entry_point_callback::EntryPointsCaller, Address, CallDef, ContractCallResult, ContractEnv, EventError, OdraError, OdraResult, VmError }; -use crate::{consts, prelude::*, utils}; +use crate::{consts, prelude::*, utils, ExecutionError}; use casper_event_standard::EventInstance; use casper_types::{ bytesrepr::{Bytes, FromBytes, ToBytes}, @@ -103,17 +103,17 @@ impl Deployer for R { let contract_ident = R::ident(); match Self::try_deploy(env, init_args) { Ok(contract) => contract, - Err(OdraError::VmError(VmError::MissingArg)) => { + Err(OdraError::ExecutionError(ExecutionError::MissingArg)) => { core::panic!("Invalid init args for contract {}.", contract_ident) } - Err(_) => core::panic!("Contract init failed") + Err(e) => core::panic!("Contract init failed {:?}", e) } } fn try_deploy(env: &HostEnv, init_args: T) -> OdraResult { let contract_ident = R::ident(); if !T::validate(&contract_ident) { - return Err(OdraError::VmError(VmError::MissingArg)); + return Err(OdraError::ExecutionError(ExecutionError::MissingArg)); } let caller = R::entry_points_caller(env); diff --git a/odra-casper/livenet-env/src/livenet_contract_env.rs b/odra-casper/livenet-env/src/livenet_contract_env.rs index fd48db58..79a0522a 100644 --- a/odra-casper/livenet-env/src/livenet_contract_env.rs +++ b/odra-casper/livenet-env/src/livenet_contract_env.rs @@ -5,7 +5,7 @@ use odra_casper_rpc_client::casper_client::CasperClient; use odra_core::callstack::{Callstack, CallstackElement}; use odra_core::casper_types::bytesrepr::Bytes; use odra_core::casper_types::{CLValue, U512}; -use odra_core::prelude::*; +use odra_core::{prelude::*, ExecutionError, OdraResult}; use odra_core::{Address, OdraError}; use odra_core::{CallDef, ContractContext, ContractRegister}; use std::io::Write; @@ -112,8 +112,9 @@ impl ContractContext for LivenetContractEnv { panic!("Revert: {:?} - {}", error, revert_msg); } - fn get_named_arg_bytes(&self, name: &str) -> Bytes { - self.get_opt_named_arg_bytes(name).unwrap() + fn get_named_arg_bytes(&self, name: &str) -> OdraResult { + self.get_opt_named_arg_bytes(name) + .ok_or(OdraError::ExecutionError(ExecutionError::MissingArg)) } fn get_opt_named_arg_bytes(&self, name: &str) -> Option { diff --git a/odra-casper/test-vm/src/vm/casper_vm.rs b/odra-casper/test-vm/src/vm/casper_vm.rs index 44a0fb31..14d63f8f 100644 --- a/odra-casper/test-vm/src/vm/casper_vm.rs +++ b/odra-casper/test-vm/src/vm/casper_vm.rs @@ -221,9 +221,9 @@ impl CasperVm { let result = self.deploy_contract(&wasm_path, &init_args); if let Some(error) = result { - let odra_error = parse_error(error.clone()); + let odra_error = parse_error(error); self.error = Some(odra_error.clone()); - panic!("Revert: Contract deploy failed {:?}", error); + panic!("Revert: Contract deploy failed {:?}", odra_error); } else { let contract_package_hash = self.contract_package_hash_from_name(&package_hash_key_name); @@ -413,7 +413,6 @@ impl CasperVm { ) -> Option { self.error = None; let session_code = PathBuf::from(wasm_path); - let deploy_item = DeployItemBuilder::new() .with_empty_payment_bytes(runtime_args! {ARG_AMOUNT => *DEFAULT_PAYMENT}) .with_authorization_keys(&[self.active_account_hash()]) @@ -468,26 +467,92 @@ fn parse_error(err: engine_state::Error) -> OdraError { if let engine_state::Error::Exec(exec_err) = err { match exec_err { engine_state::ExecError::Revert(ApiError::MissingArgument) => { - OdraError::VmError(VmError::MissingArg) + OdraError::ExecutionError(ExecutionError::MissingArg) } engine_state::ExecError::Revert(ApiError::Mint(0)) => { OdraError::VmError(VmError::BalanceExceeded) } - engine_state::ExecError::Revert(ApiError::User(code)) => { - if code == ExecutionError::NonPayable.code() { + engine_state::ExecError::Revert(ApiError::User(code)) => match code { + x if x == ExecutionError::UnwrapError.code() => { + OdraError::ExecutionError(ExecutionError::UnwrapError) + } + x if x == ExecutionError::AdditionOverflow.code() => { + OdraError::ExecutionError(ExecutionError::AdditionOverflow) + } + x if x == ExecutionError::SubtractionOverflow.code() => { + OdraError::ExecutionError(ExecutionError::SubtractionOverflow) + } + x if x == ExecutionError::NonPayable.code() => { OdraError::ExecutionError(ExecutionError::NonPayable) - } else if code == ExecutionError::ReentrantCall.code() { + } + x if x == ExecutionError::TransferToContract.code() => { + OdraError::ExecutionError(ExecutionError::TransferToContract) + } + x if x == ExecutionError::ReentrantCall.code() => { OdraError::ExecutionError(ExecutionError::ReentrantCall) - } else { - OdraError::ExecutionError(ExecutionError::User(code)) } - } + x if x == ExecutionError::ContractAlreadyInstalled.code() => { + OdraError::ExecutionError(ExecutionError::ContractAlreadyInstalled) + } + x if x == ExecutionError::UnknownConstructor.code() => { + OdraError::ExecutionError(ExecutionError::UnknownConstructor) + } + x if x == ExecutionError::NativeTransferError.code() => { + OdraError::ExecutionError(ExecutionError::NativeTransferError) + } + x if x == ExecutionError::IndexOutOfBounds.code() => { + OdraError::ExecutionError(ExecutionError::IndexOutOfBounds) + } + x if x == ExecutionError::ZeroAddress.code() => { + OdraError::ExecutionError(ExecutionError::ZeroAddress) + } + x if x == ExecutionError::AddressCreationFailed.code() => { + OdraError::ExecutionError(ExecutionError::AddressCreationFailed) + } + x if x == ExecutionError::EarlyEndOfStream.code() => { + OdraError::ExecutionError(ExecutionError::EarlyEndOfStream) + } + x if x == ExecutionError::Formatting.code() => { + OdraError::ExecutionError(ExecutionError::Formatting) + } + x if x == ExecutionError::LeftOverBytes.code() => { + OdraError::ExecutionError(ExecutionError::LeftOverBytes) + } + x if x == ExecutionError::OutOfMemory.code() => { + OdraError::ExecutionError(ExecutionError::OutOfMemory) + } + x if x == ExecutionError::NotRepresentable.code() => { + OdraError::ExecutionError(ExecutionError::NotRepresentable) + } + x if x == ExecutionError::ExceededRecursionDepth.code() => { + OdraError::ExecutionError(ExecutionError::ExceededRecursionDepth) + } + x if x == ExecutionError::KeyNotFound.code() => { + OdraError::ExecutionError(ExecutionError::KeyNotFound) + } + x if x == ExecutionError::CouldNotDeserializeSignature.code() => { + OdraError::ExecutionError(ExecutionError::CouldNotDeserializeSignature) + } + x if x == ExecutionError::TypeMismatch.code() => { + OdraError::ExecutionError(ExecutionError::TypeMismatch) + } + x if x == ExecutionError::CouldNotSignMessage.code() => { + OdraError::ExecutionError(ExecutionError::CouldNotSignMessage) + } + x if x == ExecutionError::EmptyDictionaryName.code() => { + OdraError::ExecutionError(ExecutionError::EmptyDictionaryName) + } + x if x == ExecutionError::MissingArg.code() => { + OdraError::ExecutionError(ExecutionError::MissingArg) + } + _ => OdraError::ExecutionError(ExecutionError::User(code)) + }, engine_state::ExecError::InvalidContext => OdraError::VmError(VmError::InvalidContext), engine_state::ExecError::NoSuchMethod(name) => { OdraError::VmError(VmError::NoSuchMethod(name)) } engine_state::ExecError::MissingArgument { name } => { - OdraError::VmError(VmError::MissingArg) + OdraError::ExecutionError(ExecutionError::MissingArg) } _ => OdraError::VmError(VmError::Other(format!("Casper ExecError: {}", exec_err))) } diff --git a/odra-casper/wasm-env/src/wasm_contract_env.rs b/odra-casper/wasm-env/src/wasm_contract_env.rs index a32d2bb5..774e4a33 100644 --- a/odra-casper/wasm-env/src/wasm_contract_env.rs +++ b/odra-casper/wasm-env/src/wasm_contract_env.rs @@ -7,7 +7,7 @@ use odra_core::casper_types::{CLType, CLValue, BLAKE2B_DIGEST_LENGTH}; use odra_core::prelude::*; use odra_core::{casper_types, UnwrapOrRevert}; use odra_core::{Address, OdraError}; -use odra_core::{ContractContext, ContractEnv}; +use odra_core::{ContractContext, ContractEnv, ExecutionError, OdraResult}; /// ContractContext implementation for Wasm environment. #[derive(Clone)] @@ -74,12 +74,10 @@ impl ContractContext for WasmContractEnv { host_functions::revert(error.code()) } - fn get_named_arg_bytes(&self, name: &str) -> Bytes { - let result = host_functions::get_named_arg(name); - match result { - Ok(bytes) => Bytes::from(bytes), - Err(err) => runtime::revert(err) - } + fn get_named_arg_bytes(&self, name: &str) -> OdraResult { + host_functions::get_named_arg(name) + .map(Bytes::from) + .map_err(|_| OdraError::ExecutionError(ExecutionError::MissingArg)) } fn get_opt_named_arg_bytes(&self, name: &str) -> Option { diff --git a/odra-vm/src/odra_vm_contract_env.rs b/odra-vm/src/odra_vm_contract_env.rs index e0edff20..04300052 100644 --- a/odra-vm/src/odra_vm_contract_env.rs +++ b/odra-vm/src/odra_vm_contract_env.rs @@ -3,11 +3,10 @@ use blake2::digest::VariableOutput; use blake2::{Blake2b, Blake2b512, Blake2bVar, Blake2s256, Digest}; use odra_core::casper_types::{ bytesrepr::{Bytes, ToBytes}, - U512 + CLValue, U512 }; -use odra_core::casper_types::{BlockTime, CLType, CLValue}; -use odra_core::prelude::*; use odra_core::{casper_types, Address, OdraError}; +use odra_core::{prelude::*, OdraResult}; use odra_core::{CallDef, ContractContext}; use std::collections::hash_map::DefaultHasher; use std::hash::Hasher; @@ -78,12 +77,12 @@ impl ContractContext for OdraVmContractEnv { self.vm.borrow().revert(error) } - fn get_named_arg_bytes(&self, name: &str) -> Bytes { - self.vm.borrow().get_named_arg(name).unwrap().into() + fn get_named_arg_bytes(&self, name: &str) -> OdraResult { + self.vm.borrow().get_named_arg(name).map(Into::into) } fn get_opt_named_arg_bytes(&self, name: &str) -> Option { - self.vm.borrow().get_named_arg(name).map(Into::into) + self.vm.borrow().get_named_arg(name).ok().map(Into::into) } fn handle_attached_value(&self) { diff --git a/odra-vm/src/vm/odra_vm.rs b/odra-vm/src/vm/odra_vm.rs index 02a8f959..785297b9 100644 --- a/odra-vm/src/vm/odra_vm.rs +++ b/odra-vm/src/vm/odra_vm.rs @@ -122,13 +122,14 @@ impl OdraVm { /// Gets the value of the named argument. /// /// The argument must be present in the call definition. - pub fn get_named_arg(&self, name: &str) -> Option> { + pub fn get_named_arg(&self, name: &str) -> OdraResult> { match self.state.read().unwrap().callstack_tip() { CallstackElement::Account(_) => todo!(), CallstackElement::ContractCall { call_def, .. } => call_def .args() .get(name) .map(|arg| arg.inner_bytes().to_vec()) + .ok_or_else(|| OdraError::ExecutionError(ExecutionError::MissingArg)) } }