odra_core/
contract_container.rs1use crate::entry_point_callback::EntryPointsCaller;
2use crate::prelude::*;
3use crate::{CallDef, VmError};
4use casper_types::bytesrepr::Bytes;
5use casper_types::U512;
6
7#[derive(Clone)]
11pub struct ContractContainer {
12 entry_points_caller: EntryPointsCaller,
13 ctx: ExecutionContext
14}
15
16impl ContractContainer {
17 pub fn new(entry_points_caller: EntryPointsCaller) -> Self {
19 Self {
20 entry_points_caller,
21 ctx: ExecutionContext::Installation
22 }
23 }
24
25 pub(crate) fn post_install(&mut self) {
26 self.ctx = ExecutionContext::Runtime;
27 }
28
29 pub fn call(&self, call_def: CallDef) -> OdraResult<Bytes> {
31 let ep = self
33 .entry_points_caller
34 .entry_points()
35 .iter()
36 .find(|ep| ep.name == call_def.entry_point())
37 .ok_or_else(|| {
38 OdraError::VmError(VmError::NoSuchMethod(call_def.entry_point().to_owned()))
39 })?;
40 if !ep.is_payable && call_def.amount() > U512::zero() {
41 return Err(OdraError::ExecutionError(ExecutionError::NonPayable));
42 }
43 if ep.name == "init" && self.ctx == ExecutionContext::Runtime {
44 return Err(OdraError::VmError(VmError::InvalidContext));
45 }
46 self.entry_points_caller.call(call_def)
47 }
48}
49
50#[derive(PartialEq, Eq, Clone, Copy)]
51enum ExecutionContext {
52 Installation,
53 Runtime
54}
55
56#[cfg(test)]
57mod tests {
58 use super::{ContractContainer, ExecutionContext};
59 use crate::contract_context::MockContractContext;
60 use crate::entry_point_callback::{Argument, EntryPoint, EntryPointsCaller};
61 use crate::host::{HostEnv, MockHostContext};
62 use crate::{casper_types::RuntimeArgs, VmError};
63 use crate::{prelude::*, CallDef, ContractEnv};
64
65 const TEST_ENTRYPOINT: &str = "ep";
66
67 #[test]
68 fn test_call_wrong_entrypoint() {
69 let instance = ContractContainer::empty();
71
72 let call_def = CallDef::new(TEST_ENTRYPOINT, false, RuntimeArgs::new());
74 let result = instance.call(call_def);
75
76 assert!(result.is_err());
78 }
79
80 #[test]
81 fn test_call_valid_entrypoint() {
82 let instance = ContractContainer::with_entrypoint(vec![]);
84
85 let call_def = CallDef::new(TEST_ENTRYPOINT, false, RuntimeArgs::new());
87 let result = instance.call(call_def);
88
89 assert!(result.is_ok());
91 }
92
93 impl ContractContainer {
94 fn empty() -> Self {
95 let ctx = Rc::new(RefCell::new(MockHostContext::new()));
96 let env = HostEnv::new(ctx);
97 let entry_points_caller = EntryPointsCaller::new(env, vec![], |_, call_def| {
98 Err(OdraError::VmError(VmError::NoSuchMethod(
99 call_def.entry_point().to_string()
100 )))
101 });
102 Self {
103 entry_points_caller,
104 ctx: ExecutionContext::Installation
105 }
106 }
107
108 fn with_entrypoint(args: Vec<&str>) -> Self {
109 let entry_points = vec![EntryPoint::new(
110 String::from(TEST_ENTRYPOINT),
111 args.iter()
112 .map(|name| Argument::new::<u32>(String::from(*name)))
113 .collect()
114 )];
115 let mut ctx = MockHostContext::new();
116 ctx.expect_contract_env().returning(|| {
117 ContractEnv::new(0, Rc::new(RefCell::new(MockContractContext::new())))
118 });
119 let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
120
121 let entry_points_caller = EntryPointsCaller::new(env, entry_points, |_, call_def| {
122 if call_def.entry_point() == TEST_ENTRYPOINT {
123 Ok(vec![1, 2, 3].into())
124 } else {
125 Err(OdraError::VmError(VmError::NoSuchMethod(
126 call_def.entry_point().to_string()
127 )))
128 }
129 });
130
131 Self {
132 entry_points_caller,
133 ctx: ExecutionContext::Installation
134 }
135 }
136 }
137}