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