Skip to content

Commit

Permalink
validate args at deploy time
Browse files Browse the repository at this point in the history
  • Loading branch information
kpob committed Apr 26, 2024
1 parent 6253efa commit d07f430
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 44 deletions.
21 changes: 15 additions & 6 deletions core/src/contract_container.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -53,7 +53,7 @@ impl ContractContainer {
}));
}
} else {
return Err(OdraError::VmError(VmError::MissingArg));
return Err(OdraError::ExecutionError(ExecutionError::MissingArg));
}
}
Ok(())
Expand All @@ -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";

Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand All @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions core/src/contract_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down Expand Up @@ -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<Bytes>;

/// 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<Bytes>;
Expand Down
7 changes: 5 additions & 2 deletions core/src/contract_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,11 @@ impl ExecutionEnv {
/// the contract will revert.
pub fn get_named_arg<T: FromBytes + EntrypointArgument>(&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 =
Expand Down
4 changes: 2 additions & 2 deletions core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
8 changes: 4 additions & 4 deletions core/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -103,17 +103,17 @@ impl<R: HostRef + EntryPointsCallerProvider + HasIdent> 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<T: InitArgs>(env: &HostEnv, init_args: T) -> OdraResult<Self> {
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);
Expand Down
7 changes: 4 additions & 3 deletions odra-casper/livenet-env/src/livenet_contract_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Bytes> {
self.get_opt_named_arg_bytes(name)
.ok_or(OdraError::ExecutionError(ExecutionError::MissingArg))
}

fn get_opt_named_arg_bytes(&self, name: &str) -> Option<Bytes> {
Expand Down
87 changes: 76 additions & 11 deletions odra-casper/test-vm/src/vm/casper_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -413,7 +413,6 @@ impl CasperVm {
) -> Option<engine_state::Error> {
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()])
Expand Down Expand Up @@ -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)))
}
Expand Down
12 changes: 5 additions & 7 deletions odra-casper/wasm-env/src/wasm_contract_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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<Bytes> {
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<Bytes> {
Expand Down
11 changes: 5 additions & 6 deletions odra-vm/src/odra_vm_contract_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Bytes> {
self.vm.borrow().get_named_arg(name).map(Into::into)
}

fn get_opt_named_arg_bytes(&self, name: &str) -> Option<Bytes> {
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) {
Expand Down
3 changes: 2 additions & 1 deletion odra-vm/src/vm/odra_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>> {
pub fn get_named_arg(&self, name: &str) -> OdraResult<Vec<u8>> {
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))
}
}

Expand Down

0 comments on commit d07f430

Please sign in to comment.