sui_adapter_latest/static_programmable_transactions/typing/verify/
move_functions.rs1use crate::execution_mode::ExecutionMode;
5use crate::sp;
6use crate::static_programmable_transactions::{env::Env, loading::ast::Type, typing::ast as T};
7use move_binary_format::file_format::Visibility;
8use move_core_types::identifier::IdentStr;
9use move_core_types::language_storage::ModuleId;
10use sui_types::base_types::TxContextKind;
11use sui_types::error::ExecutionErrorTrait;
12use sui_types::execution_status::{CommandArgumentError, ExecutionErrorKind};
13use sui_verifier::private_generics_verifier_v2;
14
15pub fn verify<Mode: ExecutionMode>(
25 env: &Env<Mode>,
26 txn: &T::Transaction,
27) -> Result<(), Mode::Error> {
28 for c in &txn.commands {
29 command::<Mode>(env, c).map_err(|e| e.with_command_index(c.idx as usize))?;
30 }
31 Ok(())
32}
33
34fn command<Mode: ExecutionMode>(
35 env: &Env<Mode>,
36 sp!(_, c): &T::Command,
37) -> Result<(), Mode::Error> {
38 let T::Command_ {
39 command,
40 result_type: _,
41 drop_values: _,
42 incurs_post_execution_checks: _,
43 } = c;
44 match command {
45 T::Command__::MoveCall(call) => move_call::<Mode>(env, call)?,
46 T::Command__::TransferObjects(_, _)
47 | T::Command__::SplitCoins(_, _, _)
48 | T::Command__::MergeCoins(_, _, _)
49 | T::Command__::MakeMoveVec(_, _)
50 | T::Command__::Publish(_, _, _)
51 | T::Command__::Upgrade(_, _, _, _, _) => (),
52 }
53 Ok(())
54}
55
56fn move_call<Mode: ExecutionMode>(env: &Env<Mode>, call: &T::MoveCall) -> Result<(), Mode::Error> {
62 let T::MoveCall {
63 function,
64 arguments: _,
65 } = call;
66 check_signature::<Mode>(env, function)?;
67 check_tx_context::<Mode>(env, function)?;
68 check_private_generics_v2(&function.original_mid, function.name.as_ident_str())?;
69 check_visibility::<Mode>(env, function)?;
70 Ok(())
71}
72
73fn check_signature<Mode: ExecutionMode>(
74 env: &Env<Mode>,
75 function: &T::LoadedFunction,
76) -> Result<(), Mode::Error> {
77 fn check_return_type<Mode: ExecutionMode, E: ExecutionErrorTrait>(
78 idx: usize,
79 return_type: &T::Type,
80 ) -> Result<(), E> {
81 if let Type::Reference(_, _) = return_type
82 && !Mode::allow_arbitrary_values()
83 {
84 return Err(E::from_kind(
85 ExecutionErrorKind::InvalidPublicFunctionReturnType {
86 idx: checked_as!(idx, u16)?,
87 },
88 ));
89 }
90 Ok(())
91 }
92
93 if env.protocol_config.allow_references_in_ptbs() {
94 return Ok(());
95 }
96
97 for (idx, ty) in function.signature.return_.iter().enumerate() {
98 check_return_type::<Mode, Mode::Error>(idx, ty)?;
99 }
100 Ok(())
101}
102
103fn check_tx_context<Mode: ExecutionMode>(
112 env: &Env<Mode>,
113 function: &T::LoadedFunction,
114) -> Result<(), Mode::Error> {
115 if !env.protocol_config.ptb_tx_context_restrictions() {
116 return Ok(());
117 }
118 check_no_tx_context_by_value::<Mode::Error>(&function.signature.parameters)?;
119 check_tx_context_refs::<Mode::Error>(&function.signature.parameters)?;
120 check_no_tx_context_return::<Mode::Error>(&function.signature.return_)?;
121 Ok(())
122}
123
124fn check_no_tx_context_by_value<E: ExecutionErrorTrait>(parameters: &[Type]) -> Result<(), E> {
126 let Some(idx) = parameters
127 .iter()
128 .position(|param| param.is_tx_context_by_value())
129 else {
130 return Ok(());
131 };
132 Err(E::new_with_source(
133 ExecutionErrorKind::command_argument_error(
134 CommandArgumentError::InvalidTxContext,
135 checked_as!(idx, u16)?,
136 ),
137 "TxContext cannot be taken by value",
138 ))
139}
140
141fn check_tx_context_refs<E: ExecutionErrorTrait>(parameters: &[Type]) -> Result<(), E> {
144 let mut mut_idxs = parameters
145 .iter()
146 .enumerate()
147 .filter(|(_, param)| param.is_tx_context() == TxContextKind::Mutable)
148 .map(|(idx, _)| idx);
149 let Some(first_mut_idx) = mut_idxs.next() else {
150 return Ok(());
151 };
152 if let Some(second_mut_idx) = mut_idxs.next() {
153 return Err(E::new_with_source(
154 ExecutionErrorKind::command_argument_error(
155 CommandArgumentError::InvalidTxContext,
156 checked_as!(second_mut_idx, u16)?,
157 ),
158 "TxContext can be taken by mutable reference at most once",
159 ));
160 }
161 if parameters
162 .iter()
163 .any(|param| param.is_tx_context() == TxContextKind::Immutable)
164 {
165 return Err(E::new_with_source(
166 ExecutionErrorKind::command_argument_error(
167 CommandArgumentError::InvalidTxContext,
168 checked_as!(first_mut_idx, u16)?,
169 ),
170 "&mut TxContext cannot be used alongside other TxContext parameters",
171 ));
172 }
173 Ok(())
174}
175
176fn check_no_tx_context_return<E: ExecutionErrorTrait>(return_: &[Type]) -> Result<(), E> {
178 let Some(idx) = return_.iter().position(|return_ty| {
179 return_ty.is_tx_context() != TxContextKind::None || return_ty.is_tx_context_by_value()
180 }) else {
181 return Ok(());
182 };
183 Err(E::new_with_source(
184 ExecutionErrorKind::command_argument_error(
185 CommandArgumentError::InvalidTxContext,
186 checked_as!(idx, u16)?,
187 ),
188 "TxContext cannot be returned from a Move call",
189 ))
190}
191
192fn check_visibility<Mode: ExecutionMode>(
193 _env: &Env<Mode>,
194 function: &T::LoadedFunction,
195) -> Result<(), Mode::Error> {
196 let visibility = function.visibility;
197 let is_entry = function.is_entry;
198 match (visibility, is_entry) {
199 (Visibility::Private | Visibility::Friend, true) => (),
201 (Visibility::Public, true) => (),
203 (Visibility::Public, false) => (),
205 (Visibility::Private | Visibility::Friend, false) => {
207 if !Mode::allow_arbitrary_function_calls() {
208 return Err(Mode::Error::new_with_source(
209 ExecutionErrorKind::NonEntryFunctionInvoked,
210 "Can only call `entry` or `public` functions",
211 ));
212 }
213 }
214 };
215 Ok(())
216}
217
218fn check_private_generics_v2<E: ExecutionErrorTrait>(
219 callee_package: &ModuleId,
220 callee_function: &IdentStr,
221) -> Result<(), E> {
222 let callee_address = *callee_package.address();
223 let callee_module = callee_package.name();
224 let callee = (callee_address, callee_module, callee_function);
225 let Some((_f, internal_type_parameters)) = private_generics_verifier_v2::FUNCTIONS_TO_CHECK
226 .iter()
227 .find(|(f, _)| &callee == f)
228 else {
229 return Ok(());
230 };
231 let Some((internal_idx, _)) = internal_type_parameters
234 .iter()
235 .enumerate()
236 .find(|(_, is_internal)| **is_internal)
237 else {
238 return Ok(());
240 };
241 let callee_package_name = private_generics_verifier_v2::callee_package_name(&callee_address);
242 let help =
243 private_generics_verifier_v2::help_message(&callee_address, callee_module, callee_function);
244 let msg = format!(
245 "Cannot directly call function '{}::{}::{}' since type parameter #{} can \
246 only be instantiated with types defined within the caller's module.{}",
247 callee_package_name, callee_module, callee_function, internal_idx, help,
248 );
249 Err(E::new_with_source(
250 ExecutionErrorKind::NonEntryFunctionInvoked,
251 msg,
252 ))
253}