Skip to main content

sui_adapter_latest/
adapter.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4pub use checked::*;
5#[sui_macros::with_checked_arithmetic]
6mod checked {
7    use move_vm_runtime::natives::extensions::NativeExtensions;
8    use move_vm_runtime::natives::functions::{NativeFunctionTable, NativeFunctions};
9    use move_vm_runtime::runtime::MoveRuntime;
10    use std::cell::RefCell;
11    use std::rc::Rc;
12    use std::{collections::BTreeMap, sync::Arc};
13
14    use anyhow::Result;
15    use move_binary_format::file_format::CompiledModule;
16    use move_bytecode_verifier::verify_module_with_config_metered;
17    use move_bytecode_verifier_meter::{Meter, Scope};
18    use move_core_types::account_address::AccountAddress;
19    use move_vm_config::{
20        runtime::{VMConfig, VMRuntimeLimitsConfig},
21        verifier::VerifierConfig,
22    };
23    use mysten_common::debug_fatal;
24    use sui_move_natives::{object_runtime, transaction_context::TransactionContext};
25    use sui_types::error::SuiErrorKind;
26    use sui_types::metrics::BytecodeVerifierMetrics;
27    use sui_verifier::check_for_verifier_timeout;
28    use tracing::instrument;
29
30    use sui_move_natives::{
31        NativesCostTable, object_runtime::ObjectRuntime, scratch::ScratchRuntime,
32    };
33    use sui_protocol_config::ProtocolConfig;
34    use sui_types::{
35        base_types::*,
36        error::{ExecutionError, SuiError},
37        execution_status::ExecutionErrorKind,
38        metrics::ExecutionMetrics,
39        storage::RuntimeObjectResolver,
40    };
41    use sui_verifier::verifier::sui_verify_module_metered_check_timeout_only;
42
43    pub fn new_move_runtime(
44        natives: NativeFunctionTable,
45        protocol_config: &ProtocolConfig,
46    ) -> Result<MoveRuntime, SuiError> {
47        let native_functions =
48            NativeFunctions::new(natives).map_err(|_| SuiErrorKind::ExecutionInvariantViolation)?;
49        Ok(MoveRuntime::new(
50            native_functions,
51            vm_config(protocol_config),
52        ))
53    }
54
55    pub fn vm_config(protocol_config: &ProtocolConfig) -> VMConfig {
56        VMConfig {
57            verifier: protocol_config.verifier_config(/* signing_limits */ None),
58            max_binary_format_version: protocol_config.move_binary_format_version(),
59            runtime_limits_config: VMRuntimeLimitsConfig {
60                vector_len_max: protocol_config.max_move_vector_len(),
61                max_value_nest_depth: protocol_config.max_move_value_depth_as_option(),
62                hardened_otw_check: protocol_config.hardened_otw_check(),
63                package_arena_size: protocol_config.package_arena_size_in_bytes_as_option(),
64            },
65            enable_invariant_violation_check_in_swap_loc: !protocol_config
66                .disable_invariant_violation_check_in_swap_loc(),
67            check_no_extraneous_bytes_during_deserialization: protocol_config
68                .no_extraneous_module_bytes(),
69            // Don't augment errors with execution state on-chain
70            error_execution_state: false,
71            binary_config: protocol_config.binary_config(None),
72            rethrow_serialization_type_layout_errors: protocol_config
73                .rethrow_serialization_type_layout_errors(),
74            max_type_to_layout_nodes: protocol_config.max_type_to_layout_nodes_as_option(),
75            variant_nodes: protocol_config.variant_nodes(),
76            deprecate_global_storage_ops_during_deserialization: protocol_config
77                .deprecate_global_storage_ops_during_deserialization(),
78            normalize_depth_formula: protocol_config.normalize_depth_formula(),
79            charge_ld_const_abstract_size: protocol_config.charge_ld_const_abstract_size(),
80        }
81    }
82
83    pub fn new_native_extensions<'r>(
84        child_resolver: &'r dyn RuntimeObjectResolver,
85        object_funds_resolver: &'r dyn sui_types::storage::ObjectFundsResolver,
86        input_objects: BTreeMap<ObjectID, object_runtime::InputObject>,
87        is_metered: bool,
88        protocol_config: &'r ProtocolConfig,
89        metrics: Arc<ExecutionMetrics>,
90        tx_context: Rc<RefCell<TxContext>>,
91    ) -> Result<NativeExtensions<'r>, ExecutionError> {
92        let current_epoch_id: EpochId = tx_context.borrow().epoch();
93        let extensions = NativeExtensions::default();
94        let mut exts = extensions.try_borrow_mut().map_err(|_| {
95            make_invariant_violation!(
96                "Failed to mutably borrow native extensions to populate them right after creating them"
97            )
98        })?;
99        exts.add(ObjectRuntime::new(
100            child_resolver,
101            object_funds_resolver,
102            input_objects,
103            is_metered,
104            protocol_config,
105            metrics,
106            current_epoch_id,
107        ));
108        exts.add(NativesCostTable::from_protocol_config(protocol_config));
109        exts.add(ScratchRuntime::new(protocol_config));
110        exts.add(TransactionContext::new(tx_context));
111        drop(exts);
112        Ok(extensions)
113    }
114
115    /// Given a list of `modules` and an `object_id`, mutate each module's self ID (which must be
116    /// 0x0) to be `object_id`.
117    pub fn substitute_package_id(
118        modules: &mut [CompiledModule],
119        object_id: ObjectID,
120    ) -> Result<(), ExecutionError> {
121        let new_address = AccountAddress::from(object_id);
122
123        for module in modules.iter_mut() {
124            let self_handle = module.self_handle().clone();
125            let self_address_idx = self_handle.address;
126
127            let addrs = &mut module.address_identifiers;
128            let Some(address_mut) = addrs.get_mut(self_address_idx.0 as usize) else {
129                let name = module.identifier_at(self_handle.name);
130                return Err(ExecutionError::new_with_source(
131                    ExecutionErrorKind::PublishErrorNonZeroAddress,
132                    format!("Publishing module {name} with invalid address index"),
133                ));
134            };
135
136            if *address_mut != AccountAddress::ZERO {
137                let name = module.identifier_at(self_handle.name);
138                return Err(ExecutionError::new_with_source(
139                    ExecutionErrorKind::PublishErrorNonZeroAddress,
140                    format!("Publishing module {name} with non-zero address is not allowed"),
141                ));
142            };
143
144            *address_mut = new_address;
145        }
146
147        Ok(())
148    }
149
150    pub fn missing_unwrapped_msg(id: &ObjectID) -> String {
151        format!(
152            "Unable to unwrap object {}. Was unable to retrieve last known version in the parent sync",
153            id
154        )
155    }
156
157    /// Run the bytecode verifier with a meter limit
158    ///
159    /// This function only fails if the verification does not complete within the limit.  If the
160    /// modules fail to verify but verification completes within the meter limit, the function
161    /// succeeds.
162    #[instrument(level = "trace", skip_all)]
163    pub fn run_metered_move_bytecode_verifier(
164        modules: &[CompiledModule],
165        verifier_config: &VerifierConfig,
166        meter: &mut (impl Meter + ?Sized),
167        metrics: &Arc<BytecodeVerifierMetrics>,
168    ) -> Result<(), SuiError> {
169        // run the Move verifier
170        for module in modules.iter() {
171            let per_module_meter_verifier_timer = metrics
172                .verifier_runtime_per_module_success_latency
173                .start_timer();
174
175            if let Err(e) = verify_module_timeout_only(module, verifier_config, meter) {
176                // We only checked that the failure was due to timeout
177                // Discard success timer, but record timeout/failure timer
178                metrics
179                    .verifier_runtime_per_module_timeout_latency
180                    .observe(per_module_meter_verifier_timer.stop_and_discard());
181                metrics
182                    .verifier_timeout_metrics
183                    .with_label_values(&[
184                        BytecodeVerifierMetrics::OVERALL_TAG,
185                        BytecodeVerifierMetrics::TIMEOUT_TAG,
186                    ])
187                    .inc();
188
189                return Err(e);
190            };
191
192            // Save the success timer
193            per_module_meter_verifier_timer.stop_and_record();
194            metrics
195                .verifier_timeout_metrics
196                .with_label_values(&[
197                    BytecodeVerifierMetrics::OVERALL_TAG,
198                    BytecodeVerifierMetrics::SUCCESS_TAG,
199                ])
200                .inc();
201        }
202
203        Ok(())
204    }
205
206    /// Run both the Move verifier and the Sui verifier, checking just for timeouts. Returns Ok(())
207    /// if the verifier completes within the module meter limit and the ticks are successfully
208    /// transfered to the package limit (regardless of whether verification succeeds or not).
209    fn verify_module_timeout_only(
210        module: &CompiledModule,
211        verifier_config: &VerifierConfig,
212        meter: &mut (impl Meter + ?Sized),
213    ) -> Result<(), SuiError> {
214        meter.enter_scope(module.self_id().name().as_str(), Scope::Module);
215
216        if let Err(e) = verify_module_with_config_metered(verifier_config, module, meter) {
217            // Check that the status indicates metering timeout.
218            if check_for_verifier_timeout(&e.major_status()) {
219                if e.major_status()
220                    == move_core_types::vm_status::StatusCode::REFERENCE_SAFETY_INCONSISTENT
221                {
222                    let mut bytes = vec![];
223                    let _ = module.serialize_with_version(
224                        move_binary_format::file_format_common::VERSION_MAX,
225                        &mut bytes,
226                    );
227                    debug_fatal!(
228                        "Reference safety inconsistency detected in module: {:?}",
229                        bytes
230                    );
231                }
232                return Err(SuiErrorKind::ModuleVerificationFailure {
233                    error: format!("Verification timed out: {}", e),
234                }
235                .into());
236            }
237        } else if let Err(err) = sui_verify_module_metered_check_timeout_only(
238            module,
239            &BTreeMap::new(),
240            meter,
241            verifier_config,
242        ) {
243            return Err(err.into());
244        }
245
246        if meter.transfer(Scope::Module, Scope::Package, 1.0).is_err() {
247            return Err(SuiErrorKind::ModuleVerificationFailure {
248                error: "Verification timed out".to_string(),
249            }
250            .into());
251        }
252
253        Ok(())
254    }
255}