odra_mock_vm/
contract_container.rs1use std::collections::BTreeMap;
2
3use odra_types::{
4 casper_types::{NamedArg, RuntimeArgs},
5 OdraError, VmError
6};
7
8#[doc(hidden)]
9pub type EntrypointCall = fn(String, &RuntimeArgs) -> Vec<u8>;
10#[doc(hidden)]
11pub type EntrypointArgs = Vec<String>;
12
13#[derive(Default, Clone)]
14pub struct ContractContainer {
15 name: String,
16 entrypoints: BTreeMap<String, (EntrypointArgs, EntrypointCall)>,
17 constructors: BTreeMap<String, (EntrypointArgs, EntrypointCall)>
18}
19
20impl ContractContainer {
21 pub fn new(
22 name: &str,
23 entrypoints: BTreeMap<String, (EntrypointArgs, EntrypointCall)>,
24 constructors: BTreeMap<String, (EntrypointArgs, EntrypointCall)>
25 ) -> Self {
26 Self {
27 name: String::from(name),
28 entrypoints,
29 constructors
30 }
31 }
32
33 pub fn call(&self, entrypoint: String, args: &RuntimeArgs) -> Result<Vec<u8>, OdraError> {
34 if self.constructors.get(&entrypoint).is_some() {
35 return Err(OdraError::VmError(VmError::InvalidContext));
36 }
37
38 match self.entrypoints.get(&entrypoint) {
39 Some((ep_args, call)) => {
40 self.validate_args(ep_args, args)?;
41 Ok(call(self.name.clone(), args))
42 }
43 None => Err(OdraError::VmError(VmError::NoSuchMethod(entrypoint)))
44 }
45 }
46
47 pub fn call_constructor(
48 &self,
49 entrypoint: String,
50 args: &RuntimeArgs
51 ) -> Result<Vec<u8>, OdraError> {
52 match self.constructors.get(&entrypoint) {
53 Some((ep_args, call)) => {
54 self.validate_args(ep_args, args)?;
55 Ok(call(self.name.clone(), args))
56 }
57 None => Err(OdraError::VmError(VmError::NoSuchMethod(entrypoint)))
58 }
59 }
60
61 fn validate_args(&self, args: &[String], input_args: &RuntimeArgs) -> Result<(), OdraError> {
62 let named_args = input_args
63 .named_args()
64 .map(NamedArg::name)
65 .collect::<Vec<_>>();
66
67 if args
68 .iter()
69 .filter(|arg| !named_args.contains(&arg.as_str()))
70 .map(|arg| arg.to_owned())
71 .next()
72 .is_none()
73 {
74 Ok(())
75 } else {
76 Err(OdraError::VmError(VmError::MissingArg))
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use std::collections::BTreeMap;
84
85 use odra_types::{
86 casper_types::{runtime_args, RuntimeArgs},
87 OdraError, VmError
88 };
89
90 use crate::{EntrypointArgs, EntrypointCall};
91
92 use super::ContractContainer;
93
94 #[test]
95 fn test_call_wrong_entrypoint() {
96 let instance = ContractContainer::empty();
98
99 let result = instance.call(String::from("ep"), &RuntimeArgs::new());
101
102 assert!(result.is_err());
104 }
105
106 #[test]
107 fn test_call_valid_entrypoint() {
108 let ep_name = String::from("ep");
110 let instance = ContractContainer::setup_entrypoint(ep_name.clone(), vec![]);
111
112 let result = instance.call(ep_name, &RuntimeArgs::new());
114
115 assert!(result.is_ok());
117 }
118
119 #[test]
120 fn test_call_valid_entrypoint_with_wrong_arg_name() {
121 let ep_name = String::from("ep");
123 let instance = ContractContainer::setup_entrypoint(ep_name.clone(), vec!["first"]);
124
125 let args = runtime_args! { "second" => 0 };
127 let result = instance.call(ep_name, &args);
128
129 assert_eq!(result.unwrap_err(), OdraError::VmError(VmError::MissingArg));
131 }
132
133 #[test]
134 fn test_call_valid_entrypoint_with_missing_arg() {
135 let ep_name = String::from("ep");
137 let instance = ContractContainer::setup_entrypoint(ep_name.clone(), vec!["first"]);
138
139 let result = instance.call(ep_name, &RuntimeArgs::new());
141
142 assert_eq!(result.unwrap_err(), OdraError::VmError(VmError::MissingArg));
144 }
145
146 #[test]
147 #[ignore = "At the moment is impossible to find the name of all missing args."]
148 fn test_all_missing_args_are_caught() {
149 let ep_name = String::from("ep");
151 let instance =
152 ContractContainer::setup_entrypoint(ep_name.clone(), vec!["first", "second", "third"]);
153
154 let args = runtime_args! { "third" => 0 };
156 let result = instance.call(ep_name, &args);
157
158 assert_eq!(result.unwrap_err(), OdraError::VmError(VmError::MissingArg));
160 }
161
162 #[test]
163 fn test_call_valid_constructor() {
164 let name = String::from("init");
166 let instance = ContractContainer::setup_constructor(name.clone(), vec![]);
167
168 let result = instance.call_constructor(name, &RuntimeArgs::new());
170
171 assert!(result.is_ok());
173 }
174
175 #[test]
176 fn test_call_invalid_constructor() {
177 let instance = ContractContainer::empty();
179
180 let result = instance.call_constructor(String::from("c"), &RuntimeArgs::new());
182
183 assert!(result.is_err());
185 }
186
187 #[test]
188 fn test_call_valid_constructor_with_missing_arg() {
189 let name = String::from("init");
191 let instance = ContractContainer::setup_constructor(name.clone(), vec!["first"]);
192
193 let result = instance.call_constructor(name, &RuntimeArgs::new());
195
196 assert_eq!(result.unwrap_err(), OdraError::VmError(VmError::MissingArg));
198 }
199
200 #[test]
201 fn test_call_constructor_in_invalid_context() {
202 let name = String::from("init");
204 let instance = ContractContainer::setup_constructor(name.clone(), vec![]);
205
206 let result = instance.call(name, &RuntimeArgs::new());
208
209 assert!(result.is_err());
211 }
212
213 impl ContractContainer {
214 fn empty() -> Self {
215 Self {
216 name: String::from("contract"),
217 entrypoints: BTreeMap::new(),
218 constructors: BTreeMap::new()
219 }
220 }
221
222 fn setup_entrypoint(ep_name: String, args: Vec<&str>) -> Self {
223 let call: EntrypointCall = |_, _| vec![1, 2, 3];
224 let args: EntrypointArgs = args.iter().map(|arg| arg.to_string()).collect();
225
226 let mut entrypoints = BTreeMap::new();
227 entrypoints.insert(ep_name, (args, call));
228
229 Self {
230 name: String::from("contract"),
231 entrypoints,
232 constructors: BTreeMap::new()
233 }
234 }
235
236 fn setup_constructor(ep_name: String, args: Vec<&str>) -> Self {
237 let call: EntrypointCall = |_, _| vec![1, 2, 3];
238 let args: EntrypointArgs = args.iter().map(|arg| arg.to_string()).collect();
239
240 let mut constructors = BTreeMap::new();
241 constructors.insert(ep_name, (args, call));
242
243 Self {
244 name: String::from("contract"),
245 entrypoints: BTreeMap::new(),
246 constructors
247 }
248 }
249 }
250}