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 sui_types::{error::ExecutionError, SUI_FRAMEWORK_ADDRESS};
14
15use crate::{verification_failure, TEST_SCENARIO_MODULE_NAME};
16
17pub const TRANSFER_MODULE: &IdentStr = ident_str!("transfer");
18pub const EVENT_MODULE: &IdentStr = ident_str!("event");
19pub const EVENT_FUNCTION: &IdentStr = ident_str!("emit");
20pub const PUBLIC_TRANSFER_FUNCTIONS: &[&IdentStr] = &[
21 ident_str!("public_transfer"),
22 ident_str!("public_freeze_object"),
23 ident_str!("public_share_object"),
24 ident_str!("receive"),
25];
26pub const PRIVATE_TRANSFER_FUNCTIONS: &[&IdentStr] = &[
27 ident_str!("transfer"),
28 ident_str!("freeze_object"),
29 ident_str!("share_object"),
30];
31pub const TRANSFER_IMPL_FUNCTIONS: &[&IdentStr] = &[
32 ident_str!("transfer_impl"),
33 ident_str!("freeze_object_impl"),
34 ident_str!("share_object_impl"),
35 ident_str!("receive_impl"),
36];
37
38pub fn verify_module(module: &CompiledModule) -> Result<(), ExecutionError> {
49 if *module.address() == SUI_FRAMEWORK_ADDRESS
50 && module.name() == IdentStr::new(TEST_SCENARIO_MODULE_NAME).unwrap()
51 {
52 return Ok(());
55 }
56 for func_def in &module.function_defs {
58 verify_function(module, func_def).map_err(|error| {
59 verification_failure(format!(
60 "{}::{}. {}",
61 module.self_id(),
62 module.identifier_at(module.function_handle_at(func_def.function).name),
63 error
64 ))
65 })?;
66 }
67 Ok(())
68}
69
70fn verify_function(view: &CompiledModule, fdef: &FunctionDefinition) -> Result<(), String> {
71 let code = match &fdef.code {
72 None => return Ok(()),
73 Some(code) => code,
74 };
75 for instr in &code.code {
76 if let Bytecode::CallGeneric(finst_idx) = instr {
77 let FunctionInstantiation {
78 handle,
79 type_parameters,
80 } = view.function_instantiation_at(*finst_idx);
81
82 let fhandle = view.function_handle_at(*handle);
83 let mhandle = view.module_handle_at(fhandle.module);
84
85 let type_arguments = &view.signature_at(*type_parameters).0;
86 let ident = addr_module(view, mhandle);
87 if ident == (SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE) {
88 verify_private_transfer(view, fhandle, type_arguments)?
89 } else if ident == (SUI_FRAMEWORK_ADDRESS, EVENT_MODULE) {
90 verify_private_event_emit(view, fhandle, type_arguments)?
91 }
92 }
93 }
94 Ok(())
95}
96
97fn verify_private_transfer(
98 view: &CompiledModule,
99 fhandle: &FunctionHandle,
100 type_arguments: &[SignatureToken],
101) -> Result<(), String> {
102 let self_handle = view.module_handle_at(view.self_handle_idx());
103 if addr_module(view, self_handle) == (SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE) {
104 return Ok(());
105 }
106 let fident = view.identifier_at(fhandle.name);
107 if PUBLIC_TRANSFER_FUNCTIONS.contains(&fident) {
109 return Ok(());
110 }
111 if !PRIVATE_TRANSFER_FUNCTIONS.contains(&fident) {
112 debug_assert!(false, "unknown transfer function {}", fident);
114 return Err(format!("Calling unknown transfer function, {}", fident));
115 };
116
117 if type_arguments.len() != 1 {
118 debug_assert!(false, "Expected 1 type argument for {}", fident);
119 return Err(format!("Expected 1 type argument for {}", fident));
120 }
121
122 let type_arg = &type_arguments[0];
123 if !is_defined_in_current_module(view, type_arg) {
124 return Err(format!(
125 "Invalid call to '{sui}::transfer::{f}' on an object of type '{t}'. \
126 The transferred object's type must be defined in the current module. \
127 If the object has the 'store' type ability, you can use the non-internal variant \
128 instead, i.e. '{sui}::transfer::public_{f}'",
129 sui = SUI_FRAMEWORK_ADDRESS,
130 f = fident,
131 t = format_signature_token(view, type_arg),
132 ));
133 }
134
135 Ok(())
136}
137
138fn verify_private_event_emit(
139 view: &CompiledModule,
140 fhandle: &FunctionHandle,
141 type_arguments: &[SignatureToken],
142) -> Result<(), String> {
143 let fident = view.identifier_at(fhandle.name);
144 if fident != EVENT_FUNCTION {
145 debug_assert!(false, "unknown transfer function {}", fident);
146 return Err(format!("Calling unknown event function, {}", fident));
147 };
148
149 if type_arguments.len() != 1 {
150 debug_assert!(false, "Expected 1 type argument for {}", fident);
151 return Err(format!("Expected 1 type argument for {}", fident));
152 }
153
154 let type_arg = &type_arguments[0];
155 if !is_defined_in_current_module(view, type_arg) {
156 return Err(format!(
157 "Invalid call to '{}::event::{}' with an event type '{}'. \
158 The event's type must be defined in the current module",
159 SUI_FRAMEWORK_ADDRESS,
160 fident,
161 format_signature_token(view, type_arg),
162 ));
163 }
164
165 Ok(())
166}
167
168fn is_defined_in_current_module(view: &CompiledModule, type_arg: &SignatureToken) -> bool {
169 match type_arg {
170 SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
171 let idx = match type_arg {
172 SignatureToken::Datatype(idx) => *idx,
173 SignatureToken::DatatypeInstantiation(s) => s.0,
174 _ => unreachable!(),
175 };
176 let shandle = view.datatype_handle_at(idx);
177 view.self_handle_idx() == shandle.module
178 }
179 SignatureToken::TypeParameter(_)
180 | SignatureToken::Bool
181 | SignatureToken::U8
182 | SignatureToken::U16
183 | SignatureToken::U32
184 | SignatureToken::U64
185 | SignatureToken::U128
186 | SignatureToken::U256
187 | SignatureToken::Address
188 | SignatureToken::Vector(_)
189 | SignatureToken::Signer
190 | SignatureToken::Reference(_)
191 | SignatureToken::MutableReference(_) => false,
192 }
193}
194
195fn addr_module<'a>(
196 view: &'a CompiledModule,
197 mhandle: &ModuleHandle,
198) -> (AccountAddress, &'a IdentStr) {
199 let maddr = view.address_identifier_at(mhandle.address);
200 let mident = view.identifier_at(mhandle.name);
201 (*maddr, mident)
202}