1use move_binary_format::{
5 CompiledModule,
6 file_format::{
7 Bytecode, FunctionDefinition, FunctionHandle, FunctionInstantiation, ModuleHandle,
8 SignatureToken,
9 },
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::{SUI_FRAMEWORK_ADDRESS, error::ExecutionError};
15
16use crate::{TEST_SCENARIO_MODULE_NAME, verification_failure};
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 EMIT_AUTHENTICATED_FUNCTION: &IdentStr = ident_str!("emit_authenticated");
22pub const EMIT_AUTHENTICATED_IMPL_FUNCTION: &IdentStr = ident_str!("emit_authenticated_impl");
23
24pub const PRIVATE_EVENT_FUNCTIONS: &[&IdentStr] = &[EVENT_FUNCTION, EMIT_AUTHENTICATED_FUNCTION];
25pub const GET_EVENTS_TEST_FUNCTION: &IdentStr = ident_str!("events_by_type");
26pub const COIN_REGISTRY_MODULE: &IdentStr = ident_str!("coin_registry");
27pub const DYNAMIC_COIN_CREATION_FUNCTION: &IdentStr = ident_str!("new_currency");
28pub const PUBLIC_TRANSFER_FUNCTIONS: &[&IdentStr] = &[
29 ident_str!("public_transfer"),
30 ident_str!("public_freeze_object"),
31 ident_str!("public_share_object"),
32 ident_str!("public_receive"),
33 ident_str!("receiving_object_id"),
34 ident_str!("public_party_transfer"),
35];
36pub const PRIVATE_TRANSFER_FUNCTIONS: &[&IdentStr] = &[
37 ident_str!("transfer"),
38 ident_str!("freeze_object"),
39 ident_str!("share_object"),
40 ident_str!("receive"),
41 ident_str!("party_transfer"),
42];
43
44pub fn verify_module(
55 module: &CompiledModule,
56 verifier_config: &VerifierConfig,
57) -> Result<(), ExecutionError> {
58 if *module.address() == SUI_FRAMEWORK_ADDRESS
59 && module.name() == IdentStr::new(TEST_SCENARIO_MODULE_NAME).unwrap()
60 {
61 return Ok(());
64 }
65 for func_def in &module.function_defs {
67 verify_function(module, func_def, verifier_config.allow_receiving_object_id).map_err(
68 |error| {
69 verification_failure(format!(
70 "{}::{}. {}",
71 module.self_id(),
72 module.identifier_at(module.function_handle_at(func_def.function).name),
73 error
74 ))
75 },
76 )?;
77 }
78 Ok(())
79}
80
81fn verify_function(
82 view: &CompiledModule,
83 fdef: &FunctionDefinition,
84 allow_receiving_object_id: bool,
85) -> Result<(), String> {
86 let code = match &fdef.code {
87 None => return Ok(()),
88 Some(code) => code,
89 };
90 for instr in &code.code {
91 if let Bytecode::CallGeneric(finst_idx) = instr {
92 let FunctionInstantiation {
93 handle,
94 type_parameters,
95 } = view.function_instantiation_at(*finst_idx);
96
97 let fhandle = view.function_handle_at(*handle);
98 let mhandle = view.module_handle_at(fhandle.module);
99
100 let type_arguments = &view.signature_at(*type_parameters).0;
101 let ident = addr_module(view, mhandle);
102 if ident == (SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE) {
103 verify_private_transfer(view, fhandle, type_arguments, allow_receiving_object_id)?
104 } else if ident == (SUI_FRAMEWORK_ADDRESS, EVENT_MODULE) {
105 verify_private_event_emit(view, fhandle, type_arguments)?
106 } else if ident == (SUI_FRAMEWORK_ADDRESS, COIN_REGISTRY_MODULE) {
107 verify_dynamic_coin_creation(view, fhandle, type_arguments)?
108 }
109 }
110 }
111 Ok(())
112}
113
114fn verify_private_transfer(
115 view: &CompiledModule,
116 fhandle: &FunctionHandle,
117 type_arguments: &[SignatureToken],
118 allow_receiving_object_id: bool,
119) -> Result<(), String> {
120 let public_transfer_functions = if allow_receiving_object_id {
121 PUBLIC_TRANSFER_FUNCTIONS
122 } else {
123 &PUBLIC_TRANSFER_FUNCTIONS[..4]
125 };
126 let self_handle = view.module_handle_at(view.self_handle_idx());
127 if addr_module(view, self_handle) == (SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE) {
128 return Ok(());
129 }
130 let fident = view.identifier_at(fhandle.name);
131 if public_transfer_functions.contains(&fident) {
133 return Ok(());
134 }
135 if !PRIVATE_TRANSFER_FUNCTIONS.contains(&fident) {
136 debug_assert!(false, "unknown transfer function {}", fident);
138 return Err(format!("Calling unknown transfer function, {}", fident));
139 };
140
141 if type_arguments.len() != 1 {
142 debug_assert!(false, "Expected 1 type argument for {}", fident);
143 return Err(format!("Expected 1 type argument for {}", fident));
144 }
145
146 let type_arg = &type_arguments[0];
147 if !is_defined_in_current_module(view, type_arg) {
148 return Err(format!(
149 "Invalid call to '{sui}::transfer::{f}' on an object of type '{t}'. \
150 The transferred object's type must be defined in the current module. \
151 If the object has the 'store' type ability, you can use the non-internal variant \
152 instead, i.e. '{sui}::transfer::public_{f}'",
153 sui = SUI_FRAMEWORK_ADDRESS,
154 f = fident,
155 t = format_signature_token(view, type_arg),
156 ));
157 }
158
159 Ok(())
160}
161
162fn verify_private_event_emit(
163 view: &CompiledModule,
164 fhandle: &FunctionHandle,
165 type_arguments: &[SignatureToken],
166) -> Result<(), String> {
167 let fident = view.identifier_at(fhandle.name);
168 if fident == GET_EVENTS_TEST_FUNCTION {
169 return Ok(());
171 }
172
173 if fident == EMIT_AUTHENTICATED_IMPL_FUNCTION {
174 let module_id = view.self_id();
175 if (module_id.address(), module_id.name()) != (&SUI_FRAMEWORK_ADDRESS, EVENT_MODULE) {
176 debug_assert!(
177 false,
178 "Calling {} outside of {} module this shouldn't happen",
179 EMIT_AUTHENTICATED_IMPL_FUNCTION, EVENT_MODULE
180 );
181 return Err(format!(
182 "Calling {} outside of {} which is impossible",
183 EMIT_AUTHENTICATED_IMPL_FUNCTION, EVENT_MODULE
184 ));
185 } else {
186 return Ok(());
187 }
188 }
189
190 if !PRIVATE_EVENT_FUNCTIONS.contains(&fident) {
191 debug_assert!(false, "unknown event function {}", fident);
192 return Err(format!("Calling unknown event function, {}", fident));
193 };
194
195 if type_arguments.len() != 1 {
196 debug_assert!(false, "Expected 1 type argument for {}", fident);
197 return Err(format!("Expected 1 type argument for {}", fident));
198 }
199
200 let type_arg = &type_arguments[0];
201 if !is_defined_in_current_module(view, type_arg) {
202 return Err(format!(
203 "Invalid call to '{}::event::{}' with an event type '{}'. \
204 The event's type must be defined in the current module",
205 SUI_FRAMEWORK_ADDRESS,
206 fident,
207 format_signature_token(view, type_arg),
208 ));
209 }
210
211 Ok(())
212}
213
214fn verify_dynamic_coin_creation(
217 view: &CompiledModule,
218 fhandle: &FunctionHandle,
219 type_arguments: &[SignatureToken],
220) -> Result<(), String> {
221 let fident = view.identifier_at(fhandle.name);
222
223 if fident != DYNAMIC_COIN_CREATION_FUNCTION {
226 return Ok(());
227 }
228
229 if type_arguments.len() != 1 {
231 debug_assert!(false, "Expected 1 type argument for {}", fident);
232 return Err(format!("Expected 1 type argument for {}", fident));
233 }
234
235 let type_arg = &type_arguments[0];
236 if !is_defined_in_current_module(view, type_arg) {
237 return Err(format!(
238 "Invalid call to '{}::coin_registry::{}' with type '{}'. \
239 The coin's type must be defined in the current module",
240 SUI_FRAMEWORK_ADDRESS,
241 fident,
242 format_signature_token(view, type_arg),
243 ));
244 }
245
246 Ok(())
247}
248
249fn is_defined_in_current_module(view: &CompiledModule, type_arg: &SignatureToken) -> bool {
250 match type_arg {
251 SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
252 let idx = match type_arg {
253 SignatureToken::Datatype(idx) => *idx,
254 SignatureToken::DatatypeInstantiation(s) => s.0,
255 _ => unreachable!(),
256 };
257 let shandle = view.datatype_handle_at(idx);
258 view.self_handle_idx() == shandle.module
259 }
260 SignatureToken::TypeParameter(_)
261 | SignatureToken::Bool
262 | SignatureToken::U8
263 | SignatureToken::U16
264 | SignatureToken::U32
265 | SignatureToken::U64
266 | SignatureToken::U128
267 | SignatureToken::U256
268 | SignatureToken::Address
269 | SignatureToken::Vector(_)
270 | SignatureToken::Signer
271 | SignatureToken::Reference(_)
272 | SignatureToken::MutableReference(_) => false,
273 }
274}
275
276fn addr_module<'a>(
277 view: &'a CompiledModule,
278 mhandle: &ModuleHandle,
279) -> (AccountAddress, &'a IdentStr) {
280 let maddr = view.address_identifier_at(mhandle.address);
281 let mident = view.identifier_at(mhandle.name);
282 (*maddr, mident)
283}