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