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