1use iota_types::{
6 IOTA_FRAMEWORK_ADDRESS,
7 base_types::{TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME, TxContext, TxContextKind},
8 clock::Clock,
9 error::ExecutionError,
10 is_object, is_object_vector, is_primitive,
11 move_package::{FnInfoMap, is_test_fun},
12 randomness_state::is_mutable_random,
13 transfer::Receiving,
14};
15use move_binary_format::{
16 CompiledModule,
17 file_format::{AbilitySet, Bytecode, FunctionDefinition, SignatureToken, Visibility},
18};
19use move_bytecode_utils::format_signature_token;
20
21use crate::{INIT_FN_NAME, verification_failure};
22
23pub fn verify_module(
43 module: &CompiledModule,
44 fn_info_map: &FnInfoMap,
45) -> Result<(), ExecutionError> {
46 for func_def in &module.function_defs {
50 let handle = module.function_handle_at(func_def.function);
51 let name = module.identifier_at(handle.name);
52
53 if !is_test_fun(name, module, fn_info_map) {
55 verify_init_not_called(module, func_def).map_err(verification_failure)?;
56 }
57
58 if name == INIT_FN_NAME {
59 verify_init_function(module, func_def).map_err(verification_failure)?;
60 continue;
61 }
62
63 if !func_def.is_entry {
66 continue;
68 }
69 verify_entry_function_impl(module, func_def).map_err(verification_failure)?;
70 }
71 Ok(())
72}
73
74fn verify_init_not_called(
75 module: &CompiledModule,
76 fdef: &FunctionDefinition,
77) -> Result<(), String> {
78 let code = match &fdef.code {
79 None => return Ok(()),
80 Some(code) => code,
81 };
82 code.code
83 .iter()
84 .enumerate()
85 .filter_map(|(idx, instr)| match instr {
86 Bytecode::Call(fhandle_idx) => Some((idx, module.function_handle_at(*fhandle_idx))),
87 Bytecode::CallGeneric(finst_idx) => {
88 let finst = module.function_instantiation_at(*finst_idx);
89 Some((idx, module.function_handle_at(finst.handle)))
90 }
91 _ => None,
92 })
93 .try_for_each(|(idx, fhandle)| {
94 let name = module.identifier_at(fhandle.name);
95 if name == INIT_FN_NAME {
96 Err(format!(
97 "{}::{} at offset {}. Cannot call a module's '{}' function from another Move function",
98 module.self_id(),
99 name,
100 idx,
101 INIT_FN_NAME
102 ))
103 } else {
104 Ok(())
105 }
106 })
107}
108
109fn verify_init_function(module: &CompiledModule, fdef: &FunctionDefinition) -> Result<(), String> {
111 if fdef.visibility != Visibility::Private {
112 return Err(format!(
113 "{}. '{}' function must be private",
114 module.self_id(),
115 INIT_FN_NAME
116 ));
117 }
118
119 if fdef.is_entry {
120 return Err(format!(
121 "{}. '{}' cannot be 'entry'",
122 module.self_id(),
123 INIT_FN_NAME
124 ));
125 }
126
127 let fhandle = module.function_handle_at(fdef.function);
128 if !fhandle.type_parameters.is_empty() {
129 return Err(format!(
130 "{}. '{}' function cannot have type parameters",
131 module.self_id(),
132 INIT_FN_NAME
133 ));
134 }
135
136 if !module.signature_at(fhandle.return_).is_empty() {
137 return Err(format!(
138 "{}, '{}' function cannot have return values",
139 module.self_id(),
140 INIT_FN_NAME
141 ));
142 }
143
144 let parameters = &module.signature_at(fhandle.parameters).0;
145 if parameters.is_empty() || parameters.len() > 2 {
146 return Err(format!(
147 "Expected at least one and at most two parameters for {}::{}",
148 module.self_id(),
149 INIT_FN_NAME,
150 ));
151 }
152
153 if TxContext::kind(module, ¶meters[parameters.len() - 1]) != TxContextKind::None {
159 Ok(())
160 } else {
161 Err(format!(
162 "Expected last parameter for {0}::{1} to be &mut {2}::{3}::{4} or &{2}::{3}::{4}, \
163 but found {5}",
164 module.self_id(),
165 INIT_FN_NAME,
166 IOTA_FRAMEWORK_ADDRESS,
167 TX_CONTEXT_MODULE_NAME,
168 TX_CONTEXT_STRUCT_NAME,
169 format_signature_token(module, ¶meters[0]),
170 ))
171 }
172}
173
174fn verify_entry_function_impl(
175 view: &CompiledModule,
176 func_def: &FunctionDefinition,
177) -> Result<(), String> {
178 let handle = view.function_handle_at(func_def.function);
179 let params = view.signature_at(handle.parameters);
180
181 let all_non_ctx_params = match params.0.last() {
182 Some(last_param) if TxContext::kind(view, last_param) != TxContextKind::None => {
183 ¶ms.0[0..params.0.len() - 1]
184 }
185 _ => ¶ms.0,
186 };
187 for param in all_non_ctx_params {
188 verify_param_type(view, &handle.type_parameters, param)?;
189 }
190
191 for return_ty in &view.signature_at(handle.return_).0 {
192 verify_return_type(view, &handle.type_parameters, return_ty)?;
193 }
194
195 Ok(())
196}
197
198fn verify_return_type(
199 view: &CompiledModule,
200 type_parameters: &[AbilitySet],
201 return_ty: &SignatureToken,
202) -> Result<(), String> {
203 if matches!(
204 return_ty,
205 SignatureToken::Reference(_) | SignatureToken::MutableReference(_)
206 ) {
207 return Err("Invalid entry point return type. Expected a non reference type.".to_owned());
208 }
209 let abilities = view
210 .abilities(return_ty, type_parameters)
211 .map_err(|e| format!("Unexpected CompiledModule error: {}", e))?;
212 if abilities.has_drop() {
213 Ok(())
214 } else {
215 Err(format!(
216 "Invalid entry point return type. \
217 The specified return type does not have the 'drop' ability: {}",
218 format_signature_token(view, return_ty),
219 ))
220 }
221}
222
223fn verify_param_type(
224 view: &CompiledModule,
225 function_type_args: &[AbilitySet],
226 param: &SignatureToken,
227) -> Result<(), String> {
228 if Clock::is_mutable(view, param) {
231 return Err(format!(
232 "Invalid entry point parameter type. Clock must be passed by immutable reference. got: \
233 {}",
234 format_signature_token(view, param),
235 ));
236 }
237
238 if is_mutable_random(view, param) {
241 return Err(format!(
242 "Invalid entry point parameter type. Random must be passed by immutable reference. got: \
243 {}",
244 format_signature_token(view, param),
245 ));
246 }
247
248 if is_primitive(view, function_type_args, param)
249 || is_object(view, function_type_args, param)?
250 || is_object_vector(view, function_type_args, param)?
251 || Receiving::is_receiving(view, param)
252 {
253 Ok(())
254 } else {
255 Err(format!(
256 "Invalid entry point parameter type. Expected primitive or object type. Got: {}",
257 format_signature_token(view, param)
258 ))
259 }
260}