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