Skip to main content

iota_adapter_latest/
adapter.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5pub use checked::*;
6#[iota_macros::with_checked_arithmetic]
7mod checked {
8    use std::{cell::RefCell, collections::BTreeMap, path::PathBuf, rc::Rc, sync::Arc};
9
10    use anyhow::Result;
11    use iota_common::debug_fatal;
12    use iota_move_natives::{
13        NativesCostTable,
14        authentication_context::AuthenticationContext,
15        object_runtime::{self, ObjectRuntime},
16        transaction_context::TransactionContext,
17    };
18    use iota_protocol_config::ProtocolConfig;
19    use iota_sdk_types::ObjectId;
20    use iota_types::{
21        auth_context::AuthContext,
22        base_types::*,
23        error::{ExecutionError, ExecutionErrorKind, IotaError},
24        execution_config_utils::to_binary_config,
25        metrics::{BytecodeVerifierMetrics, LimitsMetrics},
26        move_package::ProtocolBuildConfig,
27        storage::ChildObjectResolver,
28    };
29    use iota_verifier::{
30        check_for_verifier_timeout, verifier::iota_verify_module_metered_check_timeout_only,
31    };
32    use move_binary_format::file_format::CompiledModule;
33    use move_bytecode_verifier::verify_module_with_config_metered;
34    use move_bytecode_verifier_meter::{Meter, Scope};
35    use move_core_types::account_address::AccountAddress;
36    #[cfg(feature = "tracing")]
37    use move_vm_config::runtime::VMProfilerConfig;
38    use move_vm_config::{
39        runtime::{VMConfig, VMRuntimeLimitsConfig},
40        verifier::VerifierConfig,
41    };
42    use move_vm_runtime::{
43        move_vm::MoveVM, native_extensions::NativeContextExtensions,
44        native_functions::NativeFunctionTable,
45    };
46    use tracing::instrument;
47
48    /// Creates a new instance of `MoveVM` with the specified native functions
49    /// and protocol configuration. The VM is configured using a `VMConfig`
50    /// that sets limits for vector length, value depth, and other
51    /// runtime options based on the provided `ProtocolConfig`. If gas profiling
52    /// is enabled, the function configures the profiler with the provided
53    /// path.
54    pub fn new_move_vm(
55        natives: NativeFunctionTable,
56        protocol_config: &ProtocolConfig,
57        _enable_profiler: Option<PathBuf>,
58    ) -> Result<MoveVM, IotaError> {
59        #[cfg(not(feature = "tracing"))]
60        let vm_profiler_config = None;
61        #[cfg(feature = "tracing")]
62        let vm_profiler_config = _enable_profiler.map(|path| VMProfilerConfig {
63            full_path: path,
64            track_bytecode_instructions: false,
65            use_long_function_name: false,
66        });
67        MoveVM::new_with_config(
68            natives,
69            VMConfig {
70                verifier: protocol_config.verifier_config(/* signing_limits */ None),
71                max_binary_format_version: protocol_config.move_binary_format_version(),
72                runtime_limits_config: VMRuntimeLimitsConfig {
73                    vector_len_max: protocol_config.max_move_vector_len(),
74                    max_value_nest_depth: protocol_config.max_move_value_depth_as_option(),
75                    hardened_otw_check: protocol_config.hardened_otw_check(),
76                },
77                enable_invariant_violation_check_in_swap_loc: !protocol_config
78                    .disable_invariant_violation_check_in_swap_loc(),
79                check_no_extraneous_bytes_during_deserialization: protocol_config
80                    .no_extraneous_module_bytes(),
81                profiler_config: vm_profiler_config,
82                // Don't augment errors with execution state on-chain
83                error_execution_state: false,
84                binary_config: to_binary_config(protocol_config),
85                rethrow_serialization_type_layout_errors: protocol_config
86                    .rethrow_serialization_type_layout_errors(),
87                max_type_to_layout_nodes: protocol_config.max_type_to_layout_nodes_as_option(),
88                variant_nodes: protocol_config.variant_nodes(),
89            },
90        )
91        .map_err(|_| IotaError::ExecutionInvariantViolation)
92    }
93
94    /// Creates a new set of `NativeContextExtensions`.
95    ///
96    /// Configuring extensions such as `ObjectRuntime` and
97    /// `NativesCostTable`. These extensions manage object resolution, input
98    /// objects, metering, protocol configuration, and metrics tracking.
99    /// They are available and mainly used in native function implementations
100    /// via `NativeContext` instance.
101    pub fn new_native_extensions<'r>(
102        child_resolver: &'r dyn ChildObjectResolver,
103        input_objects: BTreeMap<ObjectId, object_runtime::InputObject>,
104        is_metered: bool,
105        protocol_config: &'r ProtocolConfig,
106        metrics: Arc<LimitsMetrics>,
107        tx_context: Rc<RefCell<TxContext>>,
108        auth_context: Option<Rc<RefCell<AuthContext>>>,
109    ) -> NativeContextExtensions<'r> {
110        // When changing the list of configured extensions, make sure you also
111        // update the one used while executing `move test` command.
112        let current_epoch_id = tx_context.borrow().epoch();
113        let mut extensions = NativeContextExtensions::default();
114        extensions.add(ObjectRuntime::new(
115            child_resolver,
116            input_objects,
117            is_metered,
118            protocol_config,
119            metrics,
120            current_epoch_id,
121        ));
122        extensions.add(NativesCostTable::from_protocol_config(protocol_config));
123        extensions.add(TransactionContext::new(tx_context));
124        if let Some(auth_context) = auth_context {
125            extensions.add(AuthenticationContext::new(auth_context));
126        }
127        extensions
128    }
129
130    /// Given a list of `modules` and an `object_id`, mutate each module's self
131    /// ID (which must be 0x0) to be `object_id`.
132    pub fn substitute_package_id(
133        modules: &mut [CompiledModule],
134        object_id: ObjectId,
135    ) -> Result<(), ExecutionError> {
136        let new_address = AccountAddress::new(object_id.into_bytes());
137
138        for module in modules.iter_mut() {
139            let self_handle = module.self_handle().clone();
140            let self_address_idx = self_handle.address;
141
142            let addrs = &mut module.address_identifiers;
143            let Some(address_mut) = addrs.get_mut(self_address_idx.0 as usize) else {
144                let name = module.identifier_at(self_handle.name);
145                return Err(ExecutionError::new_with_source(
146                    ExecutionErrorKind::PublishErrorNonZeroAddress,
147                    format!("Publishing module {name} with invalid address index"),
148                ));
149            };
150
151            if *address_mut != AccountAddress::ZERO {
152                let name = module.identifier_at(self_handle.name);
153                return Err(ExecutionError::new_with_source(
154                    ExecutionErrorKind::PublishErrorNonZeroAddress,
155                    format!("Publishing module {name} with non-zero address is not allowed"),
156                ));
157            };
158
159            *address_mut = new_address;
160        }
161
162        Ok(())
163    }
164
165    /// Run the bytecode verifier with a meter limit
166    ///
167    /// This function only fails if the verification does not complete within
168    /// the limit.  If the modules fail to verify but verification completes
169    /// within the meter limit, the function succeeds.
170    #[instrument(level = "trace", skip_all)]
171    pub fn run_metered_move_bytecode_verifier(
172        modules: &[CompiledModule],
173        verifier_config: &VerifierConfig,
174        meter: &mut (impl Meter + ?Sized),
175        metrics: &Arc<BytecodeVerifierMetrics>,
176        protocol_build_config: &ProtocolBuildConfig,
177    ) -> Result<(), IotaError> {
178        // run the Move verifier
179        for module in modules.iter() {
180            let per_module_meter_verifier_timer = metrics
181                .verifier_runtime_per_module_success_latency
182                .start_timer();
183
184            if let Err(e) =
185                verify_module_timeout_only(module, verifier_config, meter, protocol_build_config)
186            {
187                // We only checked that the failure was due to timeout
188                // Discard success timer, but record timeout/failure timer
189                metrics
190                    .verifier_runtime_per_module_timeout_latency
191                    .observe(per_module_meter_verifier_timer.stop_and_discard());
192                metrics
193                    .verifier_timeout_metrics
194                    .with_label_values(&[
195                        BytecodeVerifierMetrics::OVERALL_TAG,
196                        BytecodeVerifierMetrics::TIMEOUT_TAG,
197                    ])
198                    .inc();
199
200                return Err(e);
201            };
202
203            // Save the success timer
204            per_module_meter_verifier_timer.stop_and_record();
205            metrics
206                .verifier_timeout_metrics
207                .with_label_values(&[
208                    BytecodeVerifierMetrics::OVERALL_TAG,
209                    BytecodeVerifierMetrics::SUCCESS_TAG,
210                ])
211                .inc();
212        }
213
214        Ok(())
215    }
216
217    /// Run both the Move verifier and the IOTA verifier, checking just for
218    /// timeouts. Returns Ok(()) if the verifier completes within the module
219    /// meter limit and the ticks are successfully transferred to the package
220    /// limit (regardless of whether verification succeeds or not).
221    fn verify_module_timeout_only(
222        module: &CompiledModule,
223        verifier_config: &VerifierConfig,
224        meter: &mut (impl Meter + ?Sized),
225        protocol_build_config: &ProtocolBuildConfig,
226    ) -> Result<(), IotaError> {
227        meter.enter_scope(module.self_id().name().as_str(), Scope::Module);
228
229        if let Err(e) = verify_module_with_config_metered(verifier_config, module, meter) {
230            // Check that the status indicates metering timeout.
231            if check_for_verifier_timeout(&e.major_status()) {
232                if e.major_status()
233                    == move_core_types::vm_status::StatusCode::REFERENCE_SAFETY_INCONSISTENT
234                {
235                    let mut bytes = vec![];
236                    let _ = module.serialize_with_version(
237                        move_binary_format::file_format_common::VERSION_MAX,
238                        &mut bytes,
239                    );
240                    debug_fatal!(
241                        "Reference safety inconsistency detected in module: {:?}",
242                        bytes
243                    );
244                }
245                return Err(IotaError::ModuleVerificationFailure {
246                    error: format!("Verification timed out: {e}"),
247                });
248            }
249        } else if let Err(err) = iota_verify_module_metered_check_timeout_only(
250            module,
251            &BTreeMap::new(),
252            meter,
253            protocol_build_config,
254        ) {
255            return Err(err.into());
256        }
257
258        if meter.transfer(Scope::Module, Scope::Package, 1.0).is_err() {
259            return Err(IotaError::ModuleVerificationFailure {
260                error: "Verification timed out".to_string(),
261            });
262        }
263
264        Ok(())
265    }
266}