use crate::entry_point_callback::{Argument, EntryPointsCaller};
use crate::{prelude::*, OdraResult};
use crate::{CallDef, OdraError, VmError};
use casper_types::bytesrepr::Bytes;
use casper_types::RuntimeArgs;
#[derive(Clone)]
pub struct ContractContainer {
entry_points_caller: EntryPointsCaller
}
impl ContractContainer {
pub fn new(entry_points_caller: EntryPointsCaller) -> Self {
Self {
entry_points_caller
}
}
pub fn call(&self, call_def: CallDef) -> OdraResult<Bytes> {
let ep = self
.entry_points_caller
.entry_points()
.iter()
.find(|ep| ep.name == call_def.entry_point())
.ok_or_else(|| {
OdraError::VmError(VmError::NoSuchMethod(call_def.entry_point().to_string()))
})?;
self.validate_args(&ep.args, call_def.args())?;
self.entry_points_caller.call(call_def)
}
fn validate_args(&self, args: &[Argument], input_args: &RuntimeArgs) -> OdraResult<()> {
for arg in args {
if let Some(input) = input_args
.named_args()
.find(|input| input.name() == arg.name.as_str())
{
let input_ty = input.cl_value().cl_type();
let expected_ty = &arg.ty;
if input_ty != expected_ty {
return Err(OdraError::VmError(VmError::TypeMismatch {
expected: expected_ty.clone(),
found: input_ty.clone()
}));
}
} else {
return Err(OdraError::VmError(VmError::MissingArg));
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use casper_types::CLType;
use super::ContractContainer;
use crate::contract_context::MockContractContext;
use crate::entry_point_callback::{Argument, EntryPoint, EntryPointsCaller};
use crate::host::{HostEnv, MockHostContext};
use crate::{
casper_types::{runtime_args, RuntimeArgs},
OdraError, VmError
};
use crate::{prelude::*, CallDef, ContractEnv};
const TEST_ENTRYPOINT: &str = "ep";
#[test]
fn test_call_wrong_entrypoint() {
let instance = ContractContainer::empty();
let call_def = CallDef::new(TEST_ENTRYPOINT, false, RuntimeArgs::new());
let result = instance.call(call_def);
assert!(result.is_err());
}
#[test]
fn test_call_valid_entrypoint() {
let instance = ContractContainer::with_entrypoint(vec![]);
let call_def = CallDef::new(TEST_ENTRYPOINT, false, RuntimeArgs::new());
let result = instance.call(call_def);
assert!(result.is_ok());
}
#[test]
fn test_call_valid_entrypoint_with_wrong_arg_name() {
let instance = ContractContainer::with_entrypoint(vec!["first"]);
let call_def = CallDef::new(TEST_ENTRYPOINT, false, runtime_args! { "second" => 0u32 });
let result = instance.call(call_def);
assert_eq!(result.unwrap_err(), OdraError::VmError(VmError::MissingArg));
}
#[test]
fn test_call_valid_entrypoint_with_wrong_arg_type() {
let instance = ContractContainer::with_entrypoint(vec!["first"]);
let call_def = CallDef::new(TEST_ENTRYPOINT, false, runtime_args! { "first" => true });
let result = instance.call(call_def);
assert_eq!(
result.unwrap_err(),
OdraError::VmError(VmError::TypeMismatch {
expected: CLType::U32,
found: CLType::Bool
})
);
}
#[test]
fn test_call_valid_entrypoint_with_missing_arg() {
let instance = ContractContainer::with_entrypoint(vec!["first"]);
let call_def = CallDef::new(TEST_ENTRYPOINT, false, RuntimeArgs::new());
let result = instance.call(call_def);
assert_eq!(result.unwrap_err(), OdraError::VmError(VmError::MissingArg));
}
#[test]
fn test_many_missing_args() {
let instance = ContractContainer::with_entrypoint(vec!["first", "second", "third"]);
let call_def = CallDef::new(TEST_ENTRYPOINT, false, runtime_args! { "third" => 0u32 });
let result = instance.call(call_def);
assert_eq!(result.unwrap_err(), OdraError::VmError(VmError::MissingArg));
}
impl ContractContainer {
fn empty() -> Self {
let ctx = Rc::new(RefCell::new(MockHostContext::new()));
let env = HostEnv::new(ctx);
let entry_points_caller = EntryPointsCaller::new(env, vec![], |_, call_def| {
Err(OdraError::VmError(VmError::NoSuchMethod(
call_def.entry_point().to_string()
)))
});
Self {
entry_points_caller
}
}
fn with_entrypoint(args: Vec<&str>) -> Self {
let entry_points = vec![EntryPoint::new(
String::from(TEST_ENTRYPOINT),
args.iter()
.map(|name| Argument::new::<u32>(String::from(*name)))
.collect()
)];
let mut ctx = MockHostContext::new();
ctx.expect_contract_env().returning(|| {
ContractEnv::new(0, Rc::new(RefCell::new(MockContractContext::new())))
});
let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
let entry_points_caller = EntryPointsCaller::new(env, entry_points, |_, call_def| {
if call_def.entry_point() == TEST_ENTRYPOINT {
Ok(vec![1, 2, 3].into())
} else {
Err(OdraError::VmError(VmError::NoSuchMethod(
call_def.entry_point().to_string()
)))
}
});
Self {
entry_points_caller
}
}
}
}