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