1use iota_types::{
24 BRIDGE_ADDRESS, IOTA_FRAMEWORK_ADDRESS,
25 base_types::{TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME},
26 bridge::BRIDGE_SUPPORTED_ASSET,
27 error::ExecutionError,
28 move_package::{FnInfoMap, is_test_fun},
29};
30use move_binary_format::file_format::{
31 Ability, AbilitySet, Bytecode, CompiledModule, DatatypeHandle, FunctionDefinition,
32 FunctionHandle, SignatureToken, StructDefinition,
33};
34use move_core_types::{ident_str, language_storage::ModuleId};
35
36use crate::{INIT_FN_NAME, verification_failure};
37
38pub fn verify_module(
39 module: &CompiledModule,
40 fn_info_map: &FnInfoMap,
41) -> Result<(), ExecutionError> {
42 let self_id = module.self_id();
52
53 if ModuleId::new(IOTA_FRAMEWORK_ADDRESS, ident_str!("iota").to_owned()) == self_id {
54 return Ok(());
55 }
56
57 if BRIDGE_SUPPORTED_ASSET
58 .iter()
59 .any(|token| ModuleId::new(BRIDGE_ADDRESS, ident_str!(token).to_owned()) == self_id)
60 {
61 return Ok(());
62 }
63
64 let mod_handle = module.module_handle_at(module.self_module_handle_idx);
65 let mod_name = module.identifier_at(mod_handle.name).as_str();
66 let struct_defs = &module.struct_defs;
67 let mut one_time_witness_candidate = None;
68 for def in struct_defs {
70 let struct_handle = module.datatype_handle_at(def.struct_handle);
71 let struct_name = module.identifier_at(struct_handle.name).as_str();
72 if mod_name.to_ascii_uppercase() == struct_name {
73 if let Ok(field_count) = def.declared_field_count() {
76 if field_count == 1 && def.field(0).unwrap().signature.0 == SignatureToken::Bool {
79 verify_one_time_witness(module, struct_name, struct_handle)
82 .map_err(verification_failure)?;
83 one_time_witness_candidate = Some((struct_name, struct_handle, def));
88 break; }
90 }
91 }
92 }
93 for fn_def in &module.function_defs {
94 let fn_handle = module.function_handle_at(fn_def.function);
95 let fn_name = module.identifier_at(fn_handle.name);
96 if fn_name == INIT_FN_NAME {
97 if let Some((candidate_name, candidate_handle, _)) = one_time_witness_candidate {
98 verify_init_one_time_witness(module, fn_handle, candidate_name, candidate_handle)
101 .map_err(verification_failure)?;
102 } else {
103 verify_init_single_param(module, fn_handle).map_err(verification_failure)?;
106 }
107 }
108 if let Some((candidate_name, _, def)) = one_time_witness_candidate {
109 if !is_test_fun(fn_name, module, fn_info_map) {
114 verify_no_instantiations(module, fn_def, candidate_name, def)
115 .map_err(verification_failure)?;
116 }
117 }
118 }
119
120 Ok(())
121}
122
123fn verify_one_time_witness(
126 module: &CompiledModule,
127 candidate_name: &str,
128 candidate_handle: &DatatypeHandle,
129) -> Result<(), String> {
130 let drop_set = AbilitySet::EMPTY | Ability::Drop;
132 let abilities = candidate_handle.abilities;
133 if abilities != drop_set {
134 return Err(format!(
135 "one-time witness type candidate {}::{} must have a single ability: drop",
136 module.self_id(),
137 candidate_name,
138 ));
139 }
140
141 if !candidate_handle.type_parameters.is_empty() {
142 return Err(format!(
143 "one-time witness type candidate {}::{} cannot have type parameters",
144 module.self_id(),
145 candidate_name,
146 ));
147 }
148 Ok(())
149}
150
151fn verify_init_one_time_witness(
154 module: &CompiledModule,
155 fn_handle: &FunctionHandle,
156 candidate_name: &str,
157 candidate_handle: &DatatypeHandle,
158) -> Result<(), String> {
159 let fn_sig = module.signature_at(fn_handle.parameters);
160 if fn_sig.len() != 2 || !is_one_time_witness(module, &fn_sig.0[0], candidate_handle) {
161 return Err(format!(
164 "init function of a module containing one-time witness type candidate must have \
165 {}::{} as the first parameter (a struct which has no fields or a single field of type \
166 bool)",
167 module.self_id(),
168 candidate_name,
169 ));
170 }
171
172 Ok(())
173}
174
175fn is_one_time_witness(
177 view: &CompiledModule,
178 tok: &SignatureToken,
179 candidate_handle: &DatatypeHandle,
180) -> bool {
181 matches!(tok, SignatureToken::Datatype(idx) if view.datatype_handle_at(*idx) == candidate_handle)
182}
183
184fn verify_init_single_param(
187 module: &CompiledModule,
188 fn_handle: &FunctionHandle,
189) -> Result<(), String> {
190 let fn_sig = module.signature_at(fn_handle.parameters);
191 if fn_sig.len() != 1 {
192 return Err(format!(
193 "Expected last (and at most second) parameter for {0}::{1} to be &mut {2}::{3}::{4} or \
194 &{2}::{3}::{4}; optional first parameter must be of one-time witness type whose name \
195 is the same as the capitalized module name ({5}::{6}) and which has no fields or a \
196 single field of type bool",
197 module.self_id(),
198 INIT_FN_NAME,
199 IOTA_FRAMEWORK_ADDRESS,
200 TX_CONTEXT_MODULE_NAME,
201 TX_CONTEXT_STRUCT_NAME,
202 module.self_id(),
203 module.self_id().name().as_str().to_uppercase(),
204 ));
205 }
206
207 Ok(())
208}
209
210fn verify_no_instantiations(
213 module: &CompiledModule,
214 fn_def: &FunctionDefinition,
215 struct_name: &str,
216 struct_def: &StructDefinition,
217) -> Result<(), String> {
218 if fn_def.code.is_none() {
219 return Ok(());
220 }
221 for bcode in &fn_def.code.as_ref().unwrap().code {
222 let struct_def_idx = match bcode {
223 Bytecode::Pack(idx) => idx,
224 _ => continue,
225 };
226 if module.struct_def_at(*struct_def_idx) == struct_def {
229 let fn_handle = module.function_handle_at(fn_def.function);
230 let fn_name = module.identifier_at(fn_handle.name);
231 return Err(format!(
232 "one-time witness type {}::{} is instantiated \
233 in the {}::{} function and must never be",
234 module.self_id(),
235 struct_name,
236 module.self_id(),
237 fn_name,
238 ));
239 }
240 }
241
242 Ok(())
243}