1use move_binary_format::{
5 file_format::{
6 Bytecode, FunctionDefinition, FunctionHandle, FunctionInstantiation, ModuleHandle,
7 SignatureToken,
8 },
9 CompiledModule,
10};
11use move_bytecode_utils::format_signature_token;
12use move_core_types::{account_address::AccountAddress, ident_str, identifier::IdentStr};
13use move_vm_config::verifier::VerifierConfig;
14use sui_types::{error::ExecutionError, SUI_FRAMEWORK_ADDRESS};
15
16use crate::{verification_failure, TEST_SCENARIO_MODULE_NAME};
17
18pub const TRANSFER_MODULE: &IdentStr = ident_str!("transfer");
19pub const EVENT_MODULE: &IdentStr = ident_str!("event");
20pub const EVENT_FUNCTION: &IdentStr = ident_str!("emit");
21pub const PUBLIC_TRANSFER_FUNCTIONS: &[&IdentStr] = &[
22 ident_str!("public_transfer"),
23 ident_str!("public_freeze_object"),
24 ident_str!("public_share_object"),
25 ident_str!("public_receive"),
26 ident_str!("receiving_object_id"),
27];
28pub const PRIVATE_TRANSFER_FUNCTIONS: &[&IdentStr] = &[
29 ident_str!("transfer"),
30 ident_str!("freeze_object"),
31 ident_str!("share_object"),
32 ident_str!("receive"),
33];
34pub const TRANSFER_IMPL_FUNCTIONS: &[&IdentStr] = &[
35 ident_str!("transfer_impl"),
36 ident_str!("freeze_object_impl"),
37 ident_str!("share_object_impl"),
38 ident_str!("receive_impl"),
39];
40
41pub fn verify_module(
52 module: &CompiledModule,
53 verifier_config: &VerifierConfig,
54) -> Result<(), ExecutionError> {
55 if *module.address() == SUI_FRAMEWORK_ADDRESS
56 && module.name() == IdentStr::new(TEST_SCENARIO_MODULE_NAME).unwrap()
57 {
58 return Ok(());
61 }
62 for func_def in &module.function_defs {
64 verify_function(module, func_def, verifier_config.allow_receiving_object_id).map_err(
65 |error| {
66 verification_failure(format!(
67 "{}::{}. {}",
68 module.self_id(),
69 module.identifier_at(module.function_handle_at(func_def.function).name),
70 error
71 ))
72 },
73 )?;
74 }
75 Ok(())
76}
77
78fn verify_function(
79 view: &CompiledModule,
80 fdef: &FunctionDefinition,
81 allow_receiving_object_id: bool,
82) -> Result<(), String> {
83 let code = match &fdef.code {
84 None => return Ok(()),
85 Some(code) => code,
86 };
87 for instr in &code.code {
88 if let Bytecode::CallGeneric(finst_idx) = instr {
89 let FunctionInstantiation {
90 handle,
91 type_parameters,
92 } = view.function_instantiation_at(*finst_idx);
93
94 let fhandle = view.function_handle_at(*handle);
95 let mhandle = view.module_handle_at(fhandle.module);
96
97 let type_arguments = &view.signature_at(*type_parameters).0;
98 let ident = addr_module(view, mhandle);
99 if ident == (SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE) {
100 verify_private_transfer(view, fhandle, type_arguments, allow_receiving_object_id)?
101 } else if ident == (SUI_FRAMEWORK_ADDRESS, EVENT_MODULE) {
102 verify_private_event_emit(view, fhandle, type_arguments)?
103 }
104 }
105 }
106 Ok(())
107}
108
109fn verify_private_transfer(
110 view: &CompiledModule,
111 fhandle: &FunctionHandle,
112 type_arguments: &[SignatureToken],
113 allow_receiving_object_id: bool,
114) -> Result<(), String> {
115 let public_transfer_functions = if allow_receiving_object_id {
116 PUBLIC_TRANSFER_FUNCTIONS
117 } else {
118 &PUBLIC_TRANSFER_FUNCTIONS[..PUBLIC_TRANSFER_FUNCTIONS.len() - 1]
120 };
121 let self_handle = view.module_handle_at(view.self_handle_idx());
122 if addr_module(view, self_handle) == (SUI_FRAMEWORK_ADDRESS, 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 '{sui}::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. '{sui}::transfer::public_{f}'",
148 sui = SUI_FRAMEWORK_ADDRESS,
149 f = fident,
150 t = format_signature_token(view, type_arg),
151 ));
152 }
153
154 Ok(())
155}
156
157fn verify_private_event_emit(
158 view: &CompiledModule,
159 fhandle: &FunctionHandle,
160 type_arguments: &[SignatureToken],
161) -> Result<(), String> {
162 let fident = view.identifier_at(fhandle.name);
163 if fident != EVENT_FUNCTION {
164 debug_assert!(false, "unknown transfer function {}", fident);
165 return Err(format!("Calling unknown event function, {}", fident));
166 };
167
168 if type_arguments.len() != 1 {
169 debug_assert!(false, "Expected 1 type argument for {}", fident);
170 return Err(format!("Expected 1 type argument for {}", fident));
171 }
172
173 let type_arg = &type_arguments[0];
174 if !is_defined_in_current_module(view, type_arg) {
175 return Err(format!(
176 "Invalid call to '{}::event::{}' with an event type '{}'. \
177 The event's type must be defined in the current module",
178 SUI_FRAMEWORK_ADDRESS,
179 fident,
180 format_signature_token(view, type_arg),
181 ));
182 }
183
184 Ok(())
185}
186
187fn is_defined_in_current_module(view: &CompiledModule, type_arg: &SignatureToken) -> bool {
188 match type_arg {
189 SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
190 let idx = match type_arg {
191 SignatureToken::Datatype(idx) => *idx,
192 SignatureToken::DatatypeInstantiation(s) => s.0,
193 _ => unreachable!(),
194 };
195 let shandle = view.datatype_handle_at(idx);
196 view.self_handle_idx() == shandle.module
197 }
198 SignatureToken::TypeParameter(_)
199 | SignatureToken::Bool
200 | SignatureToken::U8
201 | SignatureToken::U16
202 | SignatureToken::U32
203 | SignatureToken::U64
204 | SignatureToken::U128
205 | SignatureToken::U256
206 | SignatureToken::Address
207 | SignatureToken::Vector(_)
208 | SignatureToken::Signer
209 | SignatureToken::Reference(_)
210 | SignatureToken::MutableReference(_) => false,
211 }
212}
213
214fn addr_module<'a>(
215 view: &'a CompiledModule,
216 mhandle: &ModuleHandle,
217) -> (AccountAddress, &'a IdentStr) {
218 let maddr = view.address_identifier_at(mhandle.address);
219 let mident = view.identifier_at(mhandle.name);
220 (*maddr, mident)
221}