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