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