sui_verifier_v2/
private_generics.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use move_binary_format::{
5    file_format::{
6        Bytecode, FunctionDefinition, FunctionHandle, FunctionInstantiation, ModuleHandle,
7        SignatureToken,
8    },
9    CompiledModule,
10};
11use move_bytecode_utils::format_signature_token;
12use move_core_types::{account_address::AccountAddress, ident_str, identifier::IdentStr};
13use move_vm_config::verifier::VerifierConfig;
14use sui_types::{error::ExecutionError, SUI_FRAMEWORK_ADDRESS};
15
16use crate::{verification_failure, TEST_SCENARIO_MODULE_NAME};
17
18pub const TRANSFER_MODULE: &IdentStr = ident_str!("transfer");
19pub const EVENT_MODULE: &IdentStr = ident_str!("event");
20pub const EVENT_FUNCTION: &IdentStr = ident_str!("emit");
21pub const PUBLIC_TRANSFER_FUNCTIONS: &[&IdentStr] = &[
22    ident_str!("public_transfer"),
23    ident_str!("public_freeze_object"),
24    ident_str!("public_share_object"),
25    ident_str!("public_receive"),
26    ident_str!("receiving_object_id"),
27];
28pub const PRIVATE_TRANSFER_FUNCTIONS: &[&IdentStr] = &[
29    ident_str!("transfer"),
30    ident_str!("freeze_object"),
31    ident_str!("share_object"),
32    ident_str!("receive"),
33];
34pub const TRANSFER_IMPL_FUNCTIONS: &[&IdentStr] = &[
35    ident_str!("transfer_impl"),
36    ident_str!("freeze_object_impl"),
37    ident_str!("share_object_impl"),
38    ident_str!("receive_impl"),
39];
40
41/// All transfer functions (the functions in `sui::transfer`) are "private" in that they are
42/// restricted to the module.
43/// For example, with `transfer::transfer<T>(...)`, either:
44/// - `T` must be a type declared in the current module or
45/// - `T` must have `store`
46///
47/// Similarly, `event::emit` is also "private" to the module. Unlike the `transfer` functions, there
48/// is no relaxation for `store`
49/// Concretely, with `event::emit<T>(...)`:
50/// - `T` must be a type declared in the current module
51pub fn verify_module(
52    module: &CompiledModule,
53    verifier_config: &VerifierConfig,
54) -> Result<(), ExecutionError> {
55    if *module.address() == SUI_FRAMEWORK_ADDRESS
56        && module.name() == IdentStr::new(TEST_SCENARIO_MODULE_NAME).unwrap()
57    {
58        // exclude test_module which is a test-only module in the Sui framework which "emulates"
59        // transactional execution and needs to allow test code to bypass private generics
60        return Ok(());
61    }
62    // do not need to check the sui::transfer module itself
63    for func_def in &module.function_defs {
64        verify_function(module, func_def, verifier_config.allow_receiving_object_id).map_err(
65            |error| {
66                verification_failure(format!(
67                    "{}::{}. {}",
68                    module.self_id(),
69                    module.identifier_at(module.function_handle_at(func_def.function).name),
70                    error
71                ))
72            },
73        )?;
74    }
75    Ok(())
76}
77
78fn verify_function(
79    view: &CompiledModule,
80    fdef: &FunctionDefinition,
81    allow_receiving_object_id: bool,
82) -> Result<(), String> {
83    let code = match &fdef.code {
84        None => return Ok(()),
85        Some(code) => code,
86    };
87    for instr in &code.code {
88        if let Bytecode::CallGeneric(finst_idx) = instr {
89            let FunctionInstantiation {
90                handle,
91                type_parameters,
92            } = view.function_instantiation_at(*finst_idx);
93
94            let fhandle = view.function_handle_at(*handle);
95            let mhandle = view.module_handle_at(fhandle.module);
96
97            let type_arguments = &view.signature_at(*type_parameters).0;
98            let ident = addr_module(view, mhandle);
99            if ident == (SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE) {
100                verify_private_transfer(view, fhandle, type_arguments, allow_receiving_object_id)?
101            } else if ident == (SUI_FRAMEWORK_ADDRESS, EVENT_MODULE) {
102                verify_private_event_emit(view, fhandle, type_arguments)?
103            }
104        }
105    }
106    Ok(())
107}
108
109fn verify_private_transfer(
110    view: &CompiledModule,
111    fhandle: &FunctionHandle,
112    type_arguments: &[SignatureToken],
113    allow_receiving_object_id: bool,
114) -> Result<(), String> {
115    let public_transfer_functions = if allow_receiving_object_id {
116        PUBLIC_TRANSFER_FUNCTIONS
117    } else {
118        // Before protocol version 33, the `receiving_object_id` function was not public
119        &PUBLIC_TRANSFER_FUNCTIONS[..PUBLIC_TRANSFER_FUNCTIONS.len() - 1]
120    };
121    let self_handle = view.module_handle_at(view.self_handle_idx());
122    if addr_module(view, self_handle) == (SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE) {
123        return Ok(());
124    }
125    let fident = view.identifier_at(fhandle.name);
126    // public transfer functions require `store` and have no additional rules
127    if public_transfer_functions.contains(&fident) {
128        return Ok(());
129    }
130    if !PRIVATE_TRANSFER_FUNCTIONS.contains(&fident) {
131        // unknown function, so a bug in the implementation here
132        debug_assert!(false, "unknown transfer function {}", fident);
133        return Err(format!("Calling unknown transfer function, {}", fident));
134    };
135
136    if type_arguments.len() != 1 {
137        debug_assert!(false, "Expected 1 type argument for {}", fident);
138        return Err(format!("Expected 1 type argument for {}", fident));
139    }
140
141    let type_arg = &type_arguments[0];
142    if !is_defined_in_current_module(view, type_arg) {
143        return Err(format!(
144            "Invalid call to '{sui}::transfer::{f}' on an object of type '{t}'. \
145            The transferred object's type must be defined in the current module. \
146            If the object has the 'store' type ability, you can use the non-internal variant \
147            instead, i.e. '{sui}::transfer::public_{f}'",
148            sui = SUI_FRAMEWORK_ADDRESS,
149            f = fident,
150            t = format_signature_token(view, type_arg),
151        ));
152    }
153
154    Ok(())
155}
156
157fn verify_private_event_emit(
158    view: &CompiledModule,
159    fhandle: &FunctionHandle,
160    type_arguments: &[SignatureToken],
161) -> Result<(), String> {
162    let fident = view.identifier_at(fhandle.name);
163    if fident != EVENT_FUNCTION {
164        debug_assert!(false, "unknown transfer function {}", fident);
165        return Err(format!("Calling unknown event function, {}", fident));
166    };
167
168    if type_arguments.len() != 1 {
169        debug_assert!(false, "Expected 1 type argument for {}", fident);
170        return Err(format!("Expected 1 type argument for {}", fident));
171    }
172
173    let type_arg = &type_arguments[0];
174    if !is_defined_in_current_module(view, type_arg) {
175        return Err(format!(
176            "Invalid call to '{}::event::{}' with an event type '{}'. \
177                The event's type must be defined in the current module",
178            SUI_FRAMEWORK_ADDRESS,
179            fident,
180            format_signature_token(view, type_arg),
181        ));
182    }
183
184    Ok(())
185}
186
187fn is_defined_in_current_module(view: &CompiledModule, type_arg: &SignatureToken) -> bool {
188    match type_arg {
189        SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
190            let idx = match type_arg {
191                SignatureToken::Datatype(idx) => *idx,
192                SignatureToken::DatatypeInstantiation(s) => s.0,
193                _ => unreachable!(),
194            };
195            let shandle = view.datatype_handle_at(idx);
196            view.self_handle_idx() == shandle.module
197        }
198        SignatureToken::TypeParameter(_)
199        | SignatureToken::Bool
200        | SignatureToken::U8
201        | SignatureToken::U16
202        | SignatureToken::U32
203        | SignatureToken::U64
204        | SignatureToken::U128
205        | SignatureToken::U256
206        | SignatureToken::Address
207        | SignatureToken::Vector(_)
208        | SignatureToken::Signer
209        | SignatureToken::Reference(_)
210        | SignatureToken::MutableReference(_) => false,
211    }
212}
213
214fn addr_module<'a>(
215    view: &'a CompiledModule,
216    mhandle: &ModuleHandle,
217) -> (AccountAddress, &'a IdentStr) {
218    let maddr = view.address_identifier_at(mhandle.address);
219    let mident = view.identifier_at(mhandle.name);
220    (*maddr, mident)
221}