sui_adapter_latest/static_programmable_transactions/
env.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module defines the shared environment, `Env`, used for the compilation/translation and
5//! execution of programmable transactions. While the "context" for each pass might be different,
6//! the `Env` provides consistent access to shared components such as the VM or the protocol config.
7
8use crate::{
9    data_store::{PackageStore, cached_package_store::CachedPackageStore},
10    execution_mode::ExecutionMode,
11    execution_value::ExecutionState,
12    static_programmable_transactions::{
13        execution::context::subst_signature,
14        linkage::{analysis::LinkageAnalyzer, resolved_linkage::ExecutableLinkage},
15        loading::ast::{self as L, Datatype, LoadedFunction, LoadedFunctionInstantiation, Type},
16    },
17};
18use move_binary_format::{
19    errors::VMError,
20    file_format::{Ability, AbilitySet, TypeParameterIndex},
21};
22use move_core_types::{
23    annotated_value,
24    identifier::IdentStr,
25    language_storage::{ModuleId, StructTag},
26    resolver::IntraPackageName,
27    runtime_value::{self, MoveTypeLayout},
28    vm_status::StatusCode,
29};
30use move_vm_runtime::{
31    execution::{self as vm_runtime, vm::MoveVM},
32    runtime::MoveRuntime,
33};
34use std::{cell::OnceCell, marker::PhantomData, rc::Rc};
35use sui_protocol_config::ProtocolConfig;
36use sui_types::{
37    Identifier, SUI_FRAMEWORK_PACKAGE_ID, TypeTag,
38    balance::RESOLVED_BALANCE_STRUCT,
39    base_types::{ObjectID, TxContext},
40    coin::RESOLVED_COIN_STRUCT,
41    error::ExecutionErrorTrait,
42    execution_status::{ExecutionErrorKind, TypeArgumentError},
43    funds_accumulator::RESOLVED_WITHDRAWAL_STRUCT,
44    gas_coin::GasCoin,
45    move_package::{UpgradeCap, UpgradeReceipt, UpgradeTicket},
46    object::Object,
47    type_input::{StructInput, TypeInput},
48};
49
50pub struct Env<'pc, 'vm, 'state, 'linkage, 'extensions, Mode>
51where
52    Mode: ExecutionMode,
53{
54    pub protocol_config: &'pc ProtocolConfig,
55    pub vm: &'vm MoveRuntime,
56    pub state_view: &'state mut dyn ExecutionState,
57    pub linkable_store: &'linkage CachedPackageStore<'state, 'vm>,
58    pub linkage_analysis: &'linkage LinkageAnalyzer,
59    gas_coin_type: OnceCell<Type>,
60    upgrade_ticket_type: OnceCell<Type>,
61    upgrade_receipt_type: OnceCell<Type>,
62    upgrade_cap_type: OnceCell<Type>,
63    tx_context_type: OnceCell<Type>,
64    // The VM used for type resolution of input types (and types statically present in the PTB)
65    // only. This VM should only be used for resolution of input types, but should not be used for
66    // resolution around function calls, execution, or final serialization of execution values.
67    input_type_resolution_vm: &'linkage MoveVM<'extensions>,
68    _mode: PhantomData<fn() -> Mode>,
69}
70
71macro_rules! get_or_init_ty {
72    ($env:expr, $ident:ident, $tag:expr) => {{
73        let env = $env;
74        if env.$ident.get().is_none() {
75            let tag = $tag;
76            let ty = env.load_type_from_struct(&tag)?;
77            env.$ident.set(ty.clone()).unwrap();
78        }
79        Ok(env.$ident.get().unwrap().clone())
80    }};
81}
82
83impl<'pc, 'vm, 'state, 'linkage, 'extensions, Mode>
84    Env<'pc, 'vm, 'state, 'linkage, 'extensions, Mode>
85where
86    Mode: ExecutionMode,
87{
88    pub fn new(
89        protocol_config: &'pc ProtocolConfig,
90        vm: &'vm MoveRuntime,
91        state_view: &'state mut dyn ExecutionState,
92        linkable_store: &'linkage CachedPackageStore<'state, 'vm>,
93        linkage_analysis: &'linkage LinkageAnalyzer,
94        input_type_resolution_vm: &'linkage MoveVM<'extensions>,
95    ) -> Self {
96        Self {
97            protocol_config,
98            vm,
99            state_view,
100            linkable_store,
101            linkage_analysis,
102            gas_coin_type: OnceCell::new(),
103            upgrade_ticket_type: OnceCell::new(),
104            upgrade_receipt_type: OnceCell::new(),
105            upgrade_cap_type: OnceCell::new(),
106            tx_context_type: OnceCell::new(),
107            input_type_resolution_vm,
108            _mode: PhantomData,
109        }
110    }
111
112    pub fn convert_linked_vm_error(&self, e: VMError, linkage: &ExecutableLinkage) -> Mode::Error {
113        convert_vm_error(e, self.linkable_store, Some(linkage), self.protocol_config)
114    }
115
116    pub fn convert_vm_error(&self, e: VMError) -> Mode::Error {
117        convert_vm_error(e, self.linkable_store, None, self.protocol_config)
118    }
119
120    pub fn convert_type_argument_error(
121        &self,
122        idx: usize,
123        e: VMError,
124        linkage: &ExecutableLinkage,
125    ) -> Mode::Error {
126        use move_core_types::vm_status::StatusCode;
127        let argument_idx = match checked_as!(idx, TypeParameterIndex) {
128            Err(e) => return e.into(),
129            Ok(v) => v,
130        };
131        match e.major_status() {
132            StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
133                Mode::Error::from_kind(ExecutionErrorKind::TypeArityMismatch)
134            }
135            StatusCode::EXTERNAL_RESOLUTION_REQUEST_ERROR => {
136                Mode::Error::from_kind(ExecutionErrorKind::TypeArgumentError {
137                    argument_idx,
138                    kind: TypeArgumentError::TypeNotFound,
139                })
140            }
141            StatusCode::CONSTRAINT_NOT_SATISFIED => {
142                Mode::Error::from_kind(ExecutionErrorKind::TypeArgumentError {
143                    argument_idx,
144                    kind: TypeArgumentError::ConstraintNotSatisfied,
145                })
146            }
147            _ => self.convert_linked_vm_error(e, linkage),
148        }
149    }
150
151    pub fn fully_annotated_layout(
152        &self,
153        ty: &Type,
154    ) -> Result<annotated_value::MoveTypeLayout, Mode::Error> {
155        let tag: TypeTag = ty.clone().try_into().map_err(|s| {
156            Mode::Error::new_with_source(ExecutionErrorKind::VMInvariantViolation, s)
157        })?;
158        let objects = tag.all_addresses();
159        let tag_linkage = ExecutableLinkage::type_linkage::<_, Mode::Error>(
160            self.linkage_analysis.config().clone(),
161            objects.into_iter().map(ObjectID::from),
162            self.linkable_store,
163        )?;
164        self.input_type_resolution_vm
165            .annotated_type_layout(&tag)
166            .map_err(|e| self.convert_linked_vm_error(e, &tag_linkage))
167    }
168
169    pub fn runtime_layout(&self, ty: &Type) -> Result<runtime_value::MoveTypeLayout, Mode::Error> {
170        let tag: TypeTag = ty.clone().try_into().map_err(|s| {
171            Mode::Error::new_with_source(ExecutionErrorKind::VMInvariantViolation, s)
172        })?;
173        let objects = tag.all_addresses();
174        let tag_linkage = ExecutableLinkage::type_linkage::<_, Mode::Error>(
175            self.linkage_analysis.config().clone(),
176            objects.into_iter().map(ObjectID::from),
177            self.linkable_store,
178        )?;
179        self.input_type_resolution_vm
180            .runtime_type_layout(&tag)
181            .map_err(|e| self.convert_linked_vm_error(e, &tag_linkage))
182    }
183
184    pub fn load_framework_function(
185        &self,
186        module: &IdentStr,
187        function: &IdentStr,
188        type_arguments: Vec<Type>,
189    ) -> Result<LoadedFunction, Mode::Error> {
190        self.load_function(
191            SUI_FRAMEWORK_PACKAGE_ID,
192            module.to_string(),
193            function.to_string(),
194            type_arguments,
195        )
196    }
197
198    pub fn load_function(
199        &self,
200        package: ObjectID,
201        module: String,
202        function: String,
203        type_arguments: Vec<Type>,
204    ) -> Result<LoadedFunction, Mode::Error> {
205        let module = to_identifier(module)?;
206        let name = to_identifier(function)?;
207
208        let linkage = self.linkage_analysis.compute_call_linkage::<Mode::Error>(
209            &package,
210            module.as_ident_str(),
211            name.as_ident_str(),
212            &type_arguments,
213            self.linkable_store,
214        )?;
215
216        let Some(original_id) = linkage.0.resolve_to_original_id(&package) else {
217            invariant_violation!(
218                "Package ID {:?} is not found in linkage generated for that package",
219                package
220            );
221        };
222        let version_mid = ModuleId::new(package.into(), module.clone());
223        let original_mid = ModuleId::new(original_id.into(), module);
224        let loaded_type_arguments = type_arguments
225            .iter()
226            .enumerate()
227            .map(|(idx, ty)| self.load_vm_type_argument_from_adapter_type(idx, ty))
228            .collect::<Result<Vec<_>, _>>()?;
229        // NB: We cannot use the resolution VM here because the linkage for that unifies up, and if
230        // this is a private entry function, it may have been removed in future versions of the
231        // package.
232        let vm = self
233            .vm
234            .make_vm(
235                &self.linkable_store.package_store,
236                linkage.linkage_context::<Mode::Error>()?,
237            )
238            .map_err(|e| self.convert_linked_vm_error(e, &linkage))?;
239        let runtime_signature = vm
240            .function_information(&original_mid, name.as_ident_str(), &loaded_type_arguments)
241            .map_err(|e| {
242                if e.major_status() == StatusCode::EXTERNAL_RESOLUTION_REQUEST_ERROR {
243                    Mode::Error::new_with_source(
244                        ExecutionErrorKind::FunctionNotFound,
245                        format!(
246                            "Could not resolve function '{}' in module '{}'",
247                            name, &version_mid,
248                        ),
249                    )
250                } else {
251                    self.convert_linked_vm_error(e, &linkage)
252                }
253            })?;
254        let runtime_signature = subst_signature(runtime_signature, &loaded_type_arguments)
255            .map_err(|e| self.convert_linked_vm_error(e, &linkage))?;
256        let parameters = runtime_signature
257            .parameters
258            .into_iter()
259            .map(|ty| self.adapter_type_from_vm_type(&vm, &ty))
260            .collect::<Result<Vec<_>, _>>()?;
261        let return_ = runtime_signature
262            .return_
263            .into_iter()
264            .map(|ty| self.adapter_type_from_vm_type(&vm, &ty))
265            .collect::<Result<Vec<_>, _>>()?;
266        let signature = LoadedFunctionInstantiation {
267            parameters,
268            return_,
269        };
270        Ok(LoadedFunction {
271            version_mid,
272            original_mid,
273            name,
274            type_arguments,
275            signature,
276            linkage,
277            instruction_length: runtime_signature.instruction_count,
278            definition_index: runtime_signature.index,
279            visibility: runtime_signature.visibility,
280            is_entry: runtime_signature.is_entry,
281            is_native: runtime_signature.is_native,
282        })
283    }
284
285    pub fn load_type_input(&self, idx: usize, ty: TypeInput) -> Result<Type, Mode::Error> {
286        let vm_type = self.load_vm_type_from_type_input(idx, ty)?;
287        self.adapter_type_from_vm_type(self.input_type_resolution_vm, &vm_type)
288    }
289
290    pub fn load_type_tag(&self, idx: usize, ty: &TypeTag) -> Result<Type, Mode::Error> {
291        let vm_type = self.load_vm_type_from_type_tag(Some(idx), ty)?;
292        self.adapter_type_from_vm_type(self.input_type_resolution_vm, &vm_type)
293    }
294
295    /// We verify that all types in the `StructTag` are defining ID-based types.
296    pub fn load_type_from_struct(&self, tag: &StructTag) -> Result<Type, Mode::Error> {
297        let vm_type =
298            self.load_vm_type_from_type_tag(None, &TypeTag::Struct(Box::new(tag.clone())))?;
299        self.adapter_type_from_vm_type(self.input_type_resolution_vm, &vm_type)
300    }
301
302    pub fn type_layout_for_struct(&self, tag: &StructTag) -> Result<MoveTypeLayout, Mode::Error> {
303        let ty: Type = self.load_type_from_struct(tag)?;
304        self.runtime_layout(&ty)
305    }
306
307    pub fn gas_coin_type(&self) -> Result<Type, Mode::Error> {
308        get_or_init_ty!(self, gas_coin_type, GasCoin::type_())
309    }
310
311    pub fn upgrade_ticket_type(&self) -> Result<Type, Mode::Error> {
312        get_or_init_ty!(self, upgrade_ticket_type, UpgradeTicket::type_())
313    }
314
315    pub fn upgrade_receipt_type(&self) -> Result<Type, Mode::Error> {
316        get_or_init_ty!(self, upgrade_receipt_type, UpgradeReceipt::type_())
317    }
318
319    pub fn upgrade_cap_type(&self) -> Result<Type, Mode::Error> {
320        get_or_init_ty!(self, upgrade_cap_type, UpgradeCap::type_())
321    }
322
323    pub fn tx_context_type(&self) -> Result<Type, Mode::Error> {
324        get_or_init_ty!(self, tx_context_type, TxContext::type_())
325    }
326
327    pub fn coin_type(&self, inner_type: Type) -> Result<Type, Mode::Error> {
328        const COIN_ABILITIES: AbilitySet =
329            AbilitySet::singleton(Ability::Key).union(AbilitySet::singleton(Ability::Store));
330        let (a, m, n) = RESOLVED_COIN_STRUCT;
331        let module = ModuleId::new(*a, m.to_owned());
332        Ok(Type::Datatype(Rc::new(Datatype {
333            abilities: COIN_ABILITIES,
334            module,
335            name: n.to_owned(),
336            type_arguments: vec![inner_type],
337        })))
338    }
339
340    pub fn balance_type(&self, inner_type: Type) -> Result<Type, Mode::Error> {
341        const BALANCE_ABILITIES: AbilitySet = AbilitySet::singleton(Ability::Store);
342        let (a, m, n) = RESOLVED_BALANCE_STRUCT;
343        let module = ModuleId::new(*a, m.to_owned());
344        Ok(Type::Datatype(Rc::new(Datatype {
345            abilities: BALANCE_ABILITIES,
346            module,
347            name: n.to_owned(),
348            type_arguments: vec![inner_type],
349        })))
350    }
351
352    pub fn withdrawal_type(&self, inner_type: Type) -> Result<Type, Mode::Error> {
353        const WITHDRAWAL_ABILITIES: AbilitySet = AbilitySet::singleton(Ability::Drop);
354        let (a, m, n) = RESOLVED_WITHDRAWAL_STRUCT;
355        let module = ModuleId::new(*a, m.to_owned());
356        Ok(Type::Datatype(Rc::new(Datatype {
357            abilities: WITHDRAWAL_ABILITIES,
358            module,
359            name: n.to_owned(),
360            type_arguments: vec![inner_type],
361        })))
362    }
363
364    pub fn vector_type(&self, element_type: Type) -> Result<Type, Mode::Error> {
365        let abilities = AbilitySet::polymorphic_abilities(
366            AbilitySet::VECTOR,
367            [false],
368            [element_type.abilities()],
369        )
370        .map_err(|e| {
371            Mode::Error::new_with_source(ExecutionErrorKind::VMInvariantViolation, e.to_string())
372        })?;
373        Ok(Type::Vector(Rc::new(L::Vector {
374            abilities,
375            element_type,
376        })))
377    }
378
379    pub fn read_object(&self, id: &ObjectID) -> Result<&Object, Mode::Error> {
380        let Some(obj) = self.state_view.read_object(id) else {
381            // protected by transaction input checker
382            invariant_violation!("Object {:?} does not exist", id);
383        };
384        Ok(obj)
385    }
386
387    /// Takes an adapter Type and returns a VM runtime Type and the linkage for it.
388    pub fn load_vm_type_argument_from_adapter_type(
389        &self,
390        idx: usize,
391        ty: &Type,
392    ) -> Result<vm_runtime::Type, Mode::Error> {
393        self.load_vm_type_from_adapter_type(Some(idx), ty)
394    }
395
396    fn load_vm_type_from_adapter_type(
397        &self,
398        type_arg_idx: Option<usize>,
399        ty: &Type,
400    ) -> Result<vm_runtime::Type, Mode::Error> {
401        let tag: TypeTag = ty.clone().try_into().map_err(|s| {
402            Mode::Error::new_with_source(ExecutionErrorKind::VMInvariantViolation, s)
403        })?;
404        self.load_vm_type_from_type_tag(type_arg_idx, &tag)
405    }
406
407    /// Take a type tag and returns a VM runtime Type and the linkage for it.
408    fn load_vm_type_from_type_tag(
409        &self,
410        type_arg_idx: Option<usize>,
411        tag: &TypeTag,
412    ) -> Result<vm_runtime::Type, Mode::Error> {
413        fn execution_error<Mode: ExecutionMode>(
414            env: &Env<Mode>,
415            type_arg_idx: Option<usize>,
416            e: VMError,
417            linkage: &ExecutableLinkage,
418        ) -> Mode::Error {
419            if let Some(idx) = type_arg_idx {
420                env.convert_type_argument_error(idx, e, linkage)
421            } else {
422                env.convert_linked_vm_error(e, linkage)
423            }
424        }
425
426        let objects = tag.all_addresses();
427
428        let tag_linkage = ExecutableLinkage::type_linkage::<_, Mode::Error>(
429            self.linkage_analysis.config().clone(),
430            objects.iter().map(|a| ObjectID::from(*a)),
431            self.linkable_store,
432        )?;
433        let ty = self
434            .input_type_resolution_vm
435            .load_type(tag)
436            .map_err(|e| execution_error(self, type_arg_idx, e, &tag_linkage))?;
437        Ok(ty)
438    }
439
440    /// Converts a VM runtime Type to an adapter Type.
441    pub(crate) fn adapter_type_from_vm_type(
442        &self,
443        vm: &MoveVM,
444        vm_type: &vm_runtime::Type,
445    ) -> Result<Type, Mode::Error> {
446        use vm_runtime as VRT;
447
448        Ok(match vm_type {
449            VRT::Type::Bool => Type::Bool,
450            VRT::Type::U8 => Type::U8,
451            VRT::Type::U16 => Type::U16,
452            VRT::Type::U32 => Type::U32,
453            VRT::Type::U64 => Type::U64,
454            VRT::Type::U128 => Type::U128,
455            VRT::Type::U256 => Type::U256,
456            VRT::Type::Address => Type::Address,
457            VRT::Type::Signer => Type::Signer,
458
459            VRT::Type::Reference(ref_ty) => {
460                let inner_ty = self.adapter_type_from_vm_type(vm, ref_ty)?;
461                Type::Reference(false, Rc::new(inner_ty))
462            }
463            VRT::Type::MutableReference(ref_ty) => {
464                let inner_ty = self.adapter_type_from_vm_type(vm, ref_ty)?;
465                Type::Reference(true, Rc::new(inner_ty))
466            }
467
468            VRT::Type::Vector(inner) => {
469                let element_type = self.adapter_type_from_vm_type(vm, inner)?;
470                self.vector_type(element_type)?
471            }
472            VRT::Type::Datatype(_) => {
473                let type_information = vm
474                    .type_information(vm_type)
475                    .map_err(|e| self.convert_vm_error(e))?;
476                let Some(data_type_info) = type_information.datatype_info else {
477                    invariant_violation!("Expected datatype info for datatype type {:?}", vm_type);
478                };
479                let datatype = Datatype {
480                    abilities: type_information.abilities,
481                    module: ModuleId::new(data_type_info.defining_id, data_type_info.module_name),
482                    name: data_type_info.type_name,
483                    type_arguments: vec![],
484                };
485                Type::Datatype(Rc::new(datatype))
486            }
487            ty @ VRT::Type::DatatypeInstantiation(inst) => {
488                let (_, type_arguments) = &**inst;
489                let type_information = vm
490                    .type_information(ty)
491                    .map_err(|e| self.convert_vm_error(e))?;
492                let Some(data_type_info) = type_information.datatype_info else {
493                    invariant_violation!("Expected datatype info for datatype type {:?}", vm_type);
494                };
495
496                let abilities = type_information.abilities;
497                let module = ModuleId::new(data_type_info.defining_id, data_type_info.module_name);
498                let name = data_type_info.type_name;
499                let type_arguments = type_arguments
500                    .iter()
501                    .map(|t| self.adapter_type_from_vm_type(vm, t))
502                    .collect::<Result<Vec<_>, _>>()?;
503
504                Type::Datatype(Rc::new(Datatype {
505                    abilities,
506                    module,
507                    name,
508                    type_arguments,
509                }))
510            }
511
512            VRT::Type::TyParam(_) => {
513                invariant_violation!(
514                    "Unexpected type parameter in VM type: {:?}. This should not happen as we should \
515                     have resolved all type parameters before this point.",
516                    vm_type
517                );
518            }
519        })
520    }
521
522    /// Load a `TypeInput` into a VM runtime `Type` and its `Linkage`. Loading into the VM ensures
523    /// that any adapter type or type tag that results from this is properly output with defining
524    /// IDs.
525    fn load_vm_type_from_type_input(
526        &self,
527        type_arg_idx: usize,
528        ty: TypeInput,
529    ) -> Result<vm_runtime::Type, Mode::Error> {
530        fn to_type_tag_internal<Mode: ExecutionMode>(
531            env: &Env<Mode>,
532            type_arg_idx: usize,
533            ty: TypeInput,
534        ) -> Result<TypeTag, Mode::Error> {
535            Ok(match ty {
536                TypeInput::Bool => TypeTag::Bool,
537                TypeInput::U8 => TypeTag::U8,
538                TypeInput::U16 => TypeTag::U16,
539                TypeInput::U32 => TypeTag::U32,
540                TypeInput::U64 => TypeTag::U64,
541                TypeInput::U128 => TypeTag::U128,
542                TypeInput::U256 => TypeTag::U256,
543                TypeInput::Address => TypeTag::Address,
544                TypeInput::Signer => TypeTag::Signer,
545                TypeInput::Vector(type_input) => {
546                    let inner = to_type_tag_internal(env, type_arg_idx, *type_input)?;
547                    TypeTag::Vector(Box::new(inner))
548                }
549                TypeInput::Struct(struct_input) => {
550                    let StructInput {
551                        address,
552                        module,
553                        name,
554                        type_params,
555                    } = *struct_input;
556
557                    let pkg = env
558                        .linkable_store
559                        .get_package(&address.into())
560                        .ok()
561                        .flatten()
562                        .ok_or_else(|| {
563                            let argument_idx = match checked_as!(type_arg_idx, u16) {
564                                Err(e) => return e.into(),
565                                Ok(v) => v,
566                            };
567                            Mode::Error::from_kind(ExecutionErrorKind::TypeArgumentError {
568                                argument_idx,
569                                kind: TypeArgumentError::TypeNotFound,
570                            })
571                        })?;
572                    let module = to_identifier(module)?;
573                    let name = to_identifier(name)?;
574                    let tid = IntraPackageName {
575                        module_name: module,
576                        type_name: name,
577                    };
578                    let Some(resolved_address) = pkg.type_origin_table().get(&tid).cloned() else {
579                        return Err(Mode::Error::from_kind(
580                            ExecutionErrorKind::TypeArgumentError {
581                                argument_idx: checked_as!(type_arg_idx, u16)?,
582                                kind: TypeArgumentError::TypeNotFound,
583                            },
584                        ));
585                    };
586
587                    let tys = type_params
588                        .into_iter()
589                        .map(|tp| to_type_tag_internal(env, type_arg_idx, tp))
590                        .collect::<Result<Vec<_>, _>>()?;
591                    TypeTag::Struct(Box::new(StructTag {
592                        address: resolved_address,
593                        module: tid.module_name,
594                        name: tid.type_name,
595                        type_params: tys,
596                    }))
597                }
598            })
599        }
600        let tag = to_type_tag_internal(self, type_arg_idx, ty)?;
601        self.load_vm_type_from_type_tag(Some(type_arg_idx), &tag)
602    }
603}
604
605fn to_identifier<E: ExecutionErrorTrait>(name: String) -> Result<Identifier, E> {
606    Identifier::new(name)
607        .map_err(|e| E::new_with_source(ExecutionErrorKind::VMInvariantViolation, e.to_string()))
608}
609
610fn convert_vm_error<E: ExecutionErrorTrait>(
611    error: VMError,
612    store: &dyn PackageStore,
613    linkage: Option<&ExecutableLinkage>,
614    _protocol_config: &ProtocolConfig,
615) -> E {
616    use crate::error::convert_vm_error_impl;
617    convert_vm_error_impl(
618        error,
619        &|id| {
620            debug_assert!(
621                linkage.is_some(),
622                "Linkage should be set anywhere where runtime errors may occur in order to resolve abort locations to package IDs"
623            );
624            linkage
625                .and_then(|linkage| {
626                    linkage
627                        .0
628                        .linkage
629                        .get(&(*id.address()).into())
630                        .map(|new_id| ModuleId::new((*new_id).into(), id.name().to_owned()))
631                })
632                .unwrap_or_else(|| id.clone())
633        },
634        // NB: the `id` here is the original ID (and hence _not_ relocated).
635        &|id, function| {
636            debug_assert!(
637                linkage.is_some(),
638                "Linkage should be set anywhere where runtime errors may occur in order to resolve abort locations to package IDs"
639            );
640            linkage.and_then(|linkage| {
641                let version_id = linkage
642                    .0
643                    .linkage
644                    .get(&(*id.address()).into())
645                    .cloned()
646                    .unwrap_or_else(|| ObjectID::from_address(*id.address()));
647                store.get_package(&version_id).ok().flatten().and_then(|p| {
648                    p.modules().get(id).map(|module| {
649                        let module = module.compiled_module();
650                        let fdef = module.function_def_at(function);
651                        let fhandle = module.function_handle_at(fdef.function);
652                        module.identifier_at(fhandle.name).to_string()
653                    })
654                })
655            })
656        },
657    )
658    .into()
659}