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