sui_adapter_v3/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::programmable_transactions::execution::check_private_generics_v2;
6use crate::sp;
7use crate::static_programmable_transactions::{env::Env, loading::ast::Type, typing::ast as T};
8use move_binary_format::{CompiledModule, file_format::Visibility};
9use sui_types::error::ExecutionError;
10use sui_types::execution_status::ExecutionErrorKind;
11
12/// Checks the following
13/// - valid visibility for move function calls
14///   - Can be disabled under certain execution modes
15/// - private generics rules for move function calls
16/// - no references returned from move calls
17///    - Can be disabled under certain execution modes
18///    - Can be disabled via a feature flag
19pub fn verify<Mode: ExecutionMode>(env: &Env, txn: &T::Transaction) -> Result<(), ExecutionError> {
20    for c in &txn.commands {
21        command::<Mode>(env, c).map_err(|e| e.with_command_index(c.idx as usize))?;
22    }
23    Ok(())
24}
25
26fn command<Mode: ExecutionMode>(env: &Env, sp!(_, c): &T::Command) -> Result<(), ExecutionError> {
27    let T::Command_ {
28        command,
29        result_type: _,
30        drop_values: _,
31        consumed_shared_objects: _,
32    } = c;
33    match command {
34        T::Command__::MoveCall(call) => move_call::<Mode>(env, call)?,
35        T::Command__::TransferObjects(_, _)
36        | T::Command__::SplitCoins(_, _, _)
37        | T::Command__::MergeCoins(_, _, _)
38        | T::Command__::MakeMoveVec(_, _)
39        | T::Command__::Publish(_, _, _)
40        | T::Command__::Upgrade(_, _, _, _, _) => (),
41    }
42    Ok(())
43}
44
45/// Checks a move call for
46/// - valid signature (no references in return type)
47/// - valid visibility
48/// - private generics rules
49fn move_call<Mode: ExecutionMode>(env: &Env, call: &T::MoveCall) -> Result<(), ExecutionError> {
50    let T::MoveCall {
51        function,
52        arguments: _,
53    } = call;
54    check_signature::<Mode>(env, function)?;
55    check_private_generics_v2(&function.runtime_id, function.name.as_ident_str())?;
56    check_visibility::<Mode>(env, function)?;
57    Ok(())
58}
59
60fn check_signature<Mode: ExecutionMode>(
61    env: &Env,
62    function: &T::LoadedFunction,
63) -> Result<(), ExecutionError> {
64    fn check_return_type<Mode: ExecutionMode>(
65        idx: usize,
66        return_type: &T::Type,
67    ) -> Result<(), ExecutionError> {
68        if let Type::Reference(_, _) = return_type
69            && !Mode::allow_arbitrary_values()
70        {
71            return Err(ExecutionError::from_kind(
72                ExecutionErrorKind::InvalidPublicFunctionReturnType {
73                    idx: checked_as!(idx, u16)?,
74                },
75            ));
76        }
77        Ok(())
78    }
79
80    if env.protocol_config.allow_references_in_ptbs() {
81        return Ok(());
82    }
83
84    for (idx, ty) in function.signature.return_.iter().enumerate() {
85        check_return_type::<Mode>(idx, ty)?;
86    }
87    Ok(())
88}
89
90fn check_visibility<Mode: ExecutionMode>(
91    env: &Env,
92    function: &T::LoadedFunction,
93) -> Result<(), ExecutionError> {
94    let module = env.module_definition(&function.runtime_id, &function.linkage)?;
95    let module: &CompiledModule = module.as_ref();
96    let Some((_index, fdef)) = module.find_function_def_by_name(function.name.as_str()) else {
97        invariant_violation!(
98            "Could not resolve function '{}' in module {}. \
99            This should have been checked when linking",
100            &function.name,
101            module.self_id(),
102        );
103    };
104    let visibility = fdef.visibility;
105    let is_entry = fdef.is_entry;
106    match (visibility, is_entry) {
107        // can call entry
108        (Visibility::Private | Visibility::Friend, true) => (),
109        // can call public entry
110        (Visibility::Public, true) => (),
111        // can call public
112        (Visibility::Public, false) => (),
113        // cannot call private or friend if not entry
114        (Visibility::Private | Visibility::Friend, false) => {
115            if !Mode::allow_arbitrary_function_calls() {
116                return Err(ExecutionError::new_with_source(
117                    ExecutionErrorKind::NonEntryFunctionInvoked,
118                    "Can only call `entry` or `public` functions",
119                ));
120            }
121        }
122    };
123    Ok(())
124}