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