use crate::entry_point_callback::EntryPointsCaller;
use crate::prelude::*;
use crate::{CallDef, VmError};
use casper_types::bytesrepr::Bytes;
use casper_types::U512;
#[derive(Clone)]
pub struct ContractContainer {
contract_name: String,
entry_points_caller: EntryPointsCaller,
ctx: ExecutionContext
}
impl ContractContainer {
pub fn new(name: &str, entry_points_caller: EntryPointsCaller) -> Self {
Self {
contract_name: name.to_string(),
entry_points_caller,
ctx: ExecutionContext::Installation
}
}
pub(crate) fn post_install(&mut self) {
self.ctx = ExecutionContext::Runtime;
}
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_owned()))
})?;
if !ep.is_payable && call_def.amount() > U512::zero() {
return Err(OdraError::ExecutionError(ExecutionError::NonPayable));
}
if ep.name == "init" && self.ctx == ExecutionContext::Runtime {
return Err(OdraError::VmError(VmError::InvalidContext));
}
self.entry_points_caller.call(call_def)
}
pub fn name(&self) -> &str {
&self.contract_name
}
}
#[derive(PartialEq, Eq, Clone, Copy)]
enum ExecutionContext {
Installation,
Runtime
}
#[cfg(test)]
mod tests {
use super::{ContractContainer, ExecutionContext};
use crate::contract_context::MockContractContext;
use crate::entry_point_callback::{Argument, EntryPoint, EntryPointsCaller};
use crate::host::{HostEnv, MockHostContext};
use crate::{casper_types::RuntimeArgs, 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());
}
impl ContractContainer {
fn empty() -> Self {
let ctx = Rc::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 {
contract_name: "empty".to_string(),
entry_points_caller,
ctx: ExecutionContext::Installation
}
}
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(Rc::new(RefCell::new(MockContractContext::new()))));
let env = HostEnv::new(Rc::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 {
contract_name: "with_entrypoints".to_string(),
entry_points_caller,
ctx: ExecutionContext::Installation
}
}
}
}