sui_adapter_latest/static_programmable_transactions/typing/verify/
move_functions.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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::error::ExecutionError;
11use sui_types::execution_status::ExecutionErrorKind;
12use sui_verifier::private_generics_verifier_v2;
13
14/// Checks the following
15/// - valid visibility for move function calls
16///   - Can be disabled under certain execution modes
17/// - private generics rules for move function calls
18/// - no references returned from move calls
19///    - Can be disabled under certain execution modes
20///    - Can be disabled via a feature flag
21pub fn verify<Mode: ExecutionMode>(env: &Env, txn: &T::Transaction) -> Result<(), ExecutionError> {
22    for c in &txn.commands {
23        command::<Mode>(env, c).map_err(|e| e.with_command_index(c.idx as usize))?;
24    }
25    Ok(())
26}
27
28fn command<Mode: ExecutionMode>(env: &Env, sp!(_, c): &T::Command) -> Result<(), ExecutionError> {
29    let T::Command_ {
30        command,
31        result_type: _,
32        drop_values: _,
33        consumed_shared_objects: _,
34    } = c;
35    match command {
36        T::Command__::MoveCall(call) => move_call::<Mode>(env, call)?,
37        T::Command__::TransferObjects(_, _)
38        | T::Command__::SplitCoins(_, _, _)
39        | T::Command__::MergeCoins(_, _, _)
40        | T::Command__::MakeMoveVec(_, _)
41        | T::Command__::Publish(_, _, _)
42        | T::Command__::Upgrade(_, _, _, _, _) => (),
43    }
44    Ok(())
45}
46
47/// Checks a move call for
48/// - valid signature (no references in return type)
49/// - valid visibility
50/// - private generics rules
51fn move_call<Mode: ExecutionMode>(env: &Env, call: &T::MoveCall) -> Result<(), ExecutionError> {
52    let T::MoveCall {
53        function,
54        arguments: _,
55    } = call;
56    check_signature::<Mode>(env, function)?;
57    check_private_generics_v2(&function.original_mid, function.name.as_ident_str())?;
58    check_visibility::<Mode>(env, function)?;
59    Ok(())
60}
61
62fn check_signature<Mode: ExecutionMode>(
63    env: &Env,
64    function: &T::LoadedFunction,
65) -> Result<(), ExecutionError> {
66    fn check_return_type<Mode: ExecutionMode>(
67        idx: usize,
68        return_type: &T::Type,
69    ) -> Result<(), ExecutionError> {
70        if let Type::Reference(_, _) = return_type
71            && !Mode::allow_arbitrary_values()
72        {
73            return Err(ExecutionError::from_kind(
74                ExecutionErrorKind::InvalidPublicFunctionReturnType {
75                    idx: checked_as!(idx, u16)?,
76                },
77            ));
78        }
79        Ok(())
80    }
81
82    if env.protocol_config.allow_references_in_ptbs() {
83        return Ok(());
84    }
85
86    for (idx, ty) in function.signature.return_.iter().enumerate() {
87        check_return_type::<Mode>(idx, ty)?;
88    }
89    Ok(())
90}
91
92fn check_visibility<Mode: ExecutionMode>(
93    _env: &Env,
94    function: &T::LoadedFunction,
95) -> Result<(), ExecutionError> {
96    let visibility = function.visibility;
97    let is_entry = function.is_entry;
98    match (visibility, is_entry) {
99        // can call entry
100        (Visibility::Private | Visibility::Friend, true) => (),
101        // can call public entry
102        (Visibility::Public, true) => (),
103        // can call public
104        (Visibility::Public, false) => (),
105        // cannot call private or friend if not entry
106        (Visibility::Private | Visibility::Friend, false) => {
107            if !Mode::allow_arbitrary_function_calls() {
108                return Err(ExecutionError::new_with_source(
109                    ExecutionErrorKind::NonEntryFunctionInvoked,
110                    "Can only call `entry` or `public` functions",
111                ));
112            }
113        }
114    };
115    Ok(())
116}
117
118fn check_private_generics_v2(
119    callee_package: &ModuleId,
120    callee_function: &IdentStr,
121) -> Result<(), ExecutionError> {
122    let callee_address = *callee_package.address();
123    let callee_module = callee_package.name();
124    let callee = (callee_address, callee_module, callee_function);
125    let Some((_f, internal_type_parameters)) = private_generics_verifier_v2::FUNCTIONS_TO_CHECK
126        .iter()
127        .find(|(f, _)| &callee == f)
128    else {
129        return Ok(());
130    };
131    // If we find an internal type parameter, the call is automatically invalid--since we
132    // are not in a module and cannot define any types to satisfy the internal constraint.
133    let Some((internal_idx, _)) = internal_type_parameters
134        .iter()
135        .enumerate()
136        .find(|(_, is_internal)| **is_internal)
137    else {
138        // No `internal` type parameters, so it is ok to call
139        return Ok(());
140    };
141    let callee_package_name = private_generics_verifier_v2::callee_package_name(&callee_address);
142    let help =
143        private_generics_verifier_v2::help_message(&callee_address, callee_module, callee_function);
144    let msg = format!(
145        "Cannot directly call function '{}::{}::{}' since type parameter #{} can \
146                 only be instantiated with types defined within the caller's module.{}",
147        callee_package_name, callee_module, callee_function, internal_idx, help,
148    );
149    Err(ExecutionError::new_with_source(
150        ExecutionErrorKind::NonEntryFunctionInvoked,
151        msg,
152    ))
153}