1use iota_sdk_types::Address;
6use iota_types::error::ExecutionError;
7use move_binary_format::{
8 CompiledModule,
9 file_format::{
10 Bytecode, FunctionDefinition, FunctionHandle, FunctionInstantiation, ModuleHandle,
11 SignatureToken,
12 },
13};
14use move_bytecode_utils::format_signature_token;
15use move_core_types::{ident_str, identifier::IdentStr};
16
17use crate::{TEST_SCENARIO_MODULE_NAME, verification_failure};
18
19pub const TRANSFER_MODULE: &IdentStr = ident_str!("transfer");
20pub const ACCOUNT_MODULE: &IdentStr = ident_str!("account");
21pub const EVENT_MODULE: &IdentStr = ident_str!("event");
22pub const EVENT_FUNCTION: &IdentStr = ident_str!("emit");
23pub const GET_EVENTS_TEST_FUNCTION: &IdentStr = ident_str!("events_by_type");
24pub const PUBLIC_TRANSFER_FUNCTIONS: &[&IdentStr] = &[
25 ident_str!("public_transfer"),
26 ident_str!("public_freeze_object"),
27 ident_str!("public_share_object"),
28 ident_str!("public_receive"),
29 ident_str!("receiving_object_id"),
30];
31pub const PRIVATE_TRANSFER_FUNCTIONS: &[&IdentStr] = &[
32 ident_str!("transfer"),
33 ident_str!("freeze_object"),
34 ident_str!("share_object"),
35 ident_str!("receive"),
36];
37pub const TRANSFER_IMPL_FUNCTIONS: &[&IdentStr] = &[
38 ident_str!("transfer_impl"),
39 ident_str!("freeze_object_impl"),
40 ident_str!("share_object_impl"),
41 ident_str!("receive_impl"),
42];
43
44pub const PUBLIC_ACCOUNT_FUNCTIONS: &[&IdentStr] = &[
45 ident_str!("borrow_auth_function_ref_v1"),
46 ident_str!("has_auth_function_ref_v1"),
47];
48pub const PRIVATE_ACCOUNT_FUNCTIONS: &[&IdentStr] = &[
49 ident_str!("create_account_v1"),
50 ident_str!("create_immutable_account_v1"),
51 ident_str!("rotate_auth_function_ref_v1"),
52];
53
54pub fn verify_module(module: &CompiledModule) -> Result<(), ExecutionError> {
65 if module.address().as_ref() == Address::FRAMEWORK.as_bytes()
66 && module.name().as_str() == TEST_SCENARIO_MODULE_NAME
67 {
68 return Ok(());
72 }
73 for func_def in &module.function_defs {
75 verify_function(module, func_def).map_err(|error| {
76 verification_failure(format!(
77 "{}::{}. {}",
78 module.self_id(),
79 module.identifier_at(module.function_handle_at(func_def.function).name),
80 error
81 ))
82 })?;
83 }
84 Ok(())
85}
86
87fn verify_function(view: &CompiledModule, fdef: &FunctionDefinition) -> Result<(), String> {
88 let code = match &fdef.code {
89 None => return Ok(()),
90 Some(code) => code,
91 };
92 for instr in &code.code {
93 if let Bytecode::CallGeneric(finst_idx) = instr {
94 let FunctionInstantiation {
95 handle,
96 type_parameters,
97 } = view.function_instantiation_at(*finst_idx);
98
99 let fhandle = view.function_handle_at(*handle);
100 let mhandle = view.module_handle_at(fhandle.module);
101
102 let type_arguments = &view.signature_at(*type_parameters).0;
103 let ident = addr_module(view, mhandle);
104 if ident == (Address::FRAMEWORK, TRANSFER_MODULE) {
105 verify_private_transfer_module_functions(view, fhandle, type_arguments)?
106 } else if ident == (Address::FRAMEWORK, EVENT_MODULE) {
107 verify_private_event_emit(view, fhandle, type_arguments)?
108 } else if ident == (Address::FRAMEWORK, ACCOUNT_MODULE) {
109 verify_private_account_module_functions(view, fhandle, type_arguments)?
110 }
111 }
112 }
113 Ok(())
114}
115
116fn verify_private_transfer_module_functions(
117 view: &CompiledModule,
118 fhandle: &FunctionHandle,
119 type_arguments: &[SignatureToken],
120) -> Result<(), String> {
121 let self_handle = view.module_handle_at(view.self_handle_idx());
122 if addr_module(view, self_handle) == (Address::FRAMEWORK, TRANSFER_MODULE) {
123 return Ok(());
124 }
125 let fident = view.identifier_at(fhandle.name);
126 if PUBLIC_TRANSFER_FUNCTIONS.contains(&fident) {
128 return Ok(());
129 }
130 if !PRIVATE_TRANSFER_FUNCTIONS.contains(&fident) {
131 debug_assert!(false, "unknown transfer function {fident}");
133 return Err(format!("Calling unknown transfer function, {fident}"));
134 };
135
136 if type_arguments.len() != 1 {
137 debug_assert!(false, "Expected 1 type argument for {fident}");
138 return Err(format!("Expected 1 type argument for {fident}"));
139 }
140
141 let type_arg = &type_arguments[0];
142 if !is_defined_in_current_module(view, type_arg) {
143 return Err(format!(
144 "Invalid call to '{iota}::transfer::{f}' on an object of type '{t}'. \
145 The transferred object's type must be defined in the current module. \
146 If the object has the 'store' type ability, you can use the non-internal variant \
147 instead, i.e. '{iota}::transfer::public_{f}'",
148 iota = Address::FRAMEWORK,
149 f = fident,
150 t = format_signature_token(view, type_arg),
151 ));
152 }
153
154 Ok(())
155}
156
157fn verify_private_account_module_functions(
158 view: &CompiledModule,
159 fhandle: &FunctionHandle,
160 type_arguments: &[SignatureToken],
161) -> Result<(), String> {
162 let self_handle = view.module_handle_at(view.self_handle_idx());
163 if addr_module(view, self_handle) == (Address::FRAMEWORK, ACCOUNT_MODULE) {
164 return Ok(());
165 }
166 let fident = view.identifier_at(fhandle.name);
167 if PUBLIC_ACCOUNT_FUNCTIONS.contains(&fident) {
169 return Ok(());
170 }
171 if !PRIVATE_ACCOUNT_FUNCTIONS.contains(&fident) {
172 debug_assert!(false, "unknown account function {fident}");
174 return Err(format!("Calling unknown account function, {fident}"));
175 };
176
177 if type_arguments.len() != 1 {
178 debug_assert!(false, "Expected 1 type argument for {fident}");
179 return Err(format!("Expected 1 type argument for {fident}"));
180 }
181
182 let type_arg = &type_arguments[0];
183 if !is_defined_in_current_module(view, type_arg) {
184 return Err(format!(
185 "Invalid call to '{iota}::{account}::{f}' on an object of type '{t}'. \
186 The account object's type must be defined in the current module.",
187 iota = Address::FRAMEWORK,
188 account = ACCOUNT_MODULE,
189 f = fident,
190 t = format_signature_token(view, type_arg),
191 ));
192 }
193
194 Ok(())
195}
196
197fn verify_private_event_emit(
198 view: &CompiledModule,
199 fhandle: &FunctionHandle,
200 type_arguments: &[SignatureToken],
201) -> Result<(), String> {
202 let fident = view.identifier_at(fhandle.name);
203 if fident == GET_EVENTS_TEST_FUNCTION {
204 return Ok(());
206 }
207 if fident != EVENT_FUNCTION {
208 debug_assert!(false, "unknown event function {fident}");
209 return Err(format!("Calling unknown event function, {fident}"));
210 };
211
212 if type_arguments.len() != 1 {
213 debug_assert!(false, "Expected 1 type argument for {fident}");
214 return Err(format!("Expected 1 type argument for {fident}"));
215 }
216
217 let type_arg = &type_arguments[0];
218 if !is_defined_in_current_module(view, type_arg) {
219 return Err(format!(
220 "Invalid call to '{}::event::{}' with an event type '{}'. \
221 The event's type must be defined in the current module",
222 Address::FRAMEWORK,
223 fident,
224 format_signature_token(view, type_arg),
225 ));
226 }
227
228 Ok(())
229}
230
231fn is_defined_in_current_module(view: &CompiledModule, type_arg: &SignatureToken) -> bool {
232 match type_arg {
233 SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
234 let idx = match type_arg {
235 SignatureToken::Datatype(idx) => *idx,
236 SignatureToken::DatatypeInstantiation(s) => s.0,
237 _ => unreachable!(),
238 };
239 let shandle = view.datatype_handle_at(idx);
240 view.self_handle_idx() == shandle.module
241 }
242 SignatureToken::TypeParameter(_)
243 | SignatureToken::Bool
244 | SignatureToken::U8
245 | SignatureToken::U16
246 | SignatureToken::U32
247 | SignatureToken::U64
248 | SignatureToken::U128
249 | SignatureToken::U256
250 | SignatureToken::Address
251 | SignatureToken::Vector(_)
252 | SignatureToken::Signer
253 | SignatureToken::Reference(_)
254 | SignatureToken::MutableReference(_) => false,
255 }
256}
257
258fn addr_module<'a>(view: &'a CompiledModule, mhandle: &ModuleHandle) -> (Address, &'a IdentStr) {
259 let maddr = view.address_identifier_at(mhandle.address);
260 let mident = view.identifier_at(mhandle.name);
261 (Address::new(maddr.into_bytes()), mident)
262}