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