iota_types/
move_package.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    collections::{BTreeMap, BTreeSet},
7    hash::Hash,
8};
9
10use derive_more::Display;
11use fastcrypto::hash::HashFunction;
12use iota_protocol_config::ProtocolConfig;
13use move_binary_format::{
14    binary_config::BinaryConfig, file_format::CompiledModule, file_format_common::VERSION_6,
15    normalized,
16};
17use move_core_types::{
18    account_address::AccountAddress,
19    ident_str,
20    identifier::{IdentStr, Identifier},
21    language_storage::{ModuleId, StructTag},
22};
23use schemars::JsonSchema;
24use serde::{Deserialize, Serialize};
25use serde_with::{Bytes, serde_as};
26
27use crate::{
28    IOTA_FRAMEWORK_ADDRESS,
29    base_types::{ObjectID, SequenceNumber},
30    crypto::DefaultHash,
31    error::{ExecutionError, ExecutionErrorKind, IotaError, IotaResult},
32    execution_status::PackageUpgradeError,
33    id::{ID, UID},
34    object::OBJECT_START_VERSION,
35};
36
37// TODO: robust MovePackage tests
38// #[cfg(test)]
39// #[path = "unit_tests/move_package.rs"]
40// mod base_types_tests;
41
42pub const PACKAGE_MODULE_NAME: &IdentStr = ident_str!("package");
43pub const UPGRADECAP_STRUCT_NAME: &IdentStr = ident_str!("UpgradeCap");
44pub const UPGRADETICKET_STRUCT_NAME: &IdentStr = ident_str!("UpgradeTicket");
45pub const UPGRADERECEIPT_STRUCT_NAME: &IdentStr = ident_str!("UpgradeReceipt");
46
47#[derive(Clone, Debug)]
48/// Additional information about a function
49pub struct FnInfo {
50    /// If true, it's a function involved in testing (`[test]`, `[test_only]`,
51    /// `[expected_failure]`)
52    pub is_test: bool,
53}
54
55#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
56/// Uniquely identifies a function in a module
57pub struct FnInfoKey {
58    pub fn_name: String,
59    pub mod_addr: AccountAddress,
60}
61
62/// A map from function info keys to function info
63pub type FnInfoMap = BTreeMap<FnInfoKey, FnInfo>;
64
65/// Identifies a struct and the module it was defined in
66#[derive(
67    Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, Hash, JsonSchema,
68)]
69pub struct TypeOrigin {
70    pub module_name: String,
71    // `struct_name` alias to support backwards compatibility with the old name
72    #[serde(alias = "struct_name")]
73    pub datatype_name: String,
74    pub package: ObjectID,
75}
76
77/// Upgraded package info for the linkage table
78#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash, JsonSchema)]
79pub struct UpgradeInfo {
80    /// ID of the upgraded packages
81    pub upgraded_id: ObjectID,
82    /// Version of the upgraded package
83    pub upgraded_version: SequenceNumber,
84}
85
86// serde_bytes::ByteBuf is an analog of Vec<u8> with built-in fast
87// serialization.
88#[serde_as]
89#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
90pub struct MovePackage {
91    pub(crate) id: ObjectID,
92    /// Most move packages are uniquely identified by their ID (i.e. there is
93    /// only one version per ID), but the version is still stored because
94    /// one package may be an upgrade of another (at a different ID), in
95    /// which case its version will be one greater than the version of the
96    /// upgraded package.
97    ///
98    /// Framework packages are an exception to this rule -- all versions of the
99    /// framework packages exist at the same ID, at increasing versions.
100    ///
101    /// In all cases, packages are referred to by move calls using just their
102    /// ID, and they are always loaded at their latest version.
103    pub(crate) version: SequenceNumber,
104    // TODO use session cache
105    #[serde_as(as = "BTreeMap<_, Bytes>")]
106    pub(crate) module_map: BTreeMap<String, Vec<u8>>,
107
108    /// Maps struct/module to a package version where it was first defined,
109    /// stored as a vector for simple serialization and deserialization.
110    pub(crate) type_origin_table: Vec<TypeOrigin>,
111
112    // For each dependency, maps original package ID to the info about the (upgraded) dependency
113    // version that this package is using
114    pub(crate) linkage_table: BTreeMap<ObjectID, UpgradeInfo>,
115}
116
117// NB: do _not_ add `Serialize` or `Deserialize` to this enum. Convert to u8
118// first  or use the associated constants before storing in any serialization
119// setting.
120/// Rust representation of upgrade policy constants in `iota::package`.
121#[repr(u8)]
122#[derive(Display, Debug, Clone, Copy)]
123pub enum UpgradePolicy {
124    #[display("COMPATIBLE")]
125    Compatible = 0,
126    #[display("ADDITIVE")]
127    Additive = 128,
128    #[display("DEP_ONLY")]
129    DepOnly = 192,
130}
131
132impl UpgradePolicy {
133    /// Convenience accessors to the upgrade policies as u8s.
134    pub const COMPATIBLE: u8 = Self::Compatible as u8;
135    pub const ADDITIVE: u8 = Self::Additive as u8;
136    pub const DEP_ONLY: u8 = Self::DepOnly as u8;
137
138    pub fn is_valid_policy(policy: &u8) -> bool {
139        Self::try_from(*policy).is_ok()
140    }
141}
142
143impl TryFrom<u8> for UpgradePolicy {
144    type Error = ();
145    fn try_from(value: u8) -> Result<Self, Self::Error> {
146        match value {
147            x if x == Self::Compatible as u8 => Ok(Self::Compatible),
148            x if x == Self::Additive as u8 => Ok(Self::Additive),
149            x if x == Self::DepOnly as u8 => Ok(Self::DepOnly),
150            _ => Err(()),
151        }
152    }
153}
154
155/// Rust representation of `iota::package::UpgradeCap`.
156#[derive(Debug, Serialize, Deserialize)]
157pub struct UpgradeCap {
158    pub id: UID,
159    pub package: ID,
160    pub version: u64,
161    pub policy: u8,
162}
163
164/// Rust representation of `iota::package::UpgradeTicket`.
165#[derive(Debug, Serialize, Deserialize)]
166pub struct UpgradeTicket {
167    pub cap: ID,
168    pub package: ID,
169    pub policy: u8,
170    pub digest: Vec<u8>,
171}
172
173/// Rust representation of `iota::package::UpgradeReceipt`.
174#[derive(Debug, Serialize, Deserialize)]
175pub struct UpgradeReceipt {
176    pub cap: ID,
177    pub package: ID,
178}
179
180impl MovePackage {
181    /// Create a package with all required data (including serialized modules,
182    /// type origin and linkage tables) already supplied.
183    pub fn new(
184        id: ObjectID,
185        version: SequenceNumber,
186        module_map: BTreeMap<String, Vec<u8>>,
187        max_move_package_size: u64,
188        type_origin_table: Vec<TypeOrigin>,
189        linkage_table: BTreeMap<ObjectID, UpgradeInfo>,
190    ) -> Result<Self, ExecutionError> {
191        let pkg = Self {
192            id,
193            version,
194            module_map,
195            type_origin_table,
196            linkage_table,
197        };
198        let object_size = pkg.size() as u64;
199        if object_size > max_move_package_size {
200            return Err(ExecutionErrorKind::MovePackageTooBig {
201                object_size,
202                max_object_size: max_move_package_size,
203            }
204            .into());
205        }
206        Ok(pkg)
207    }
208
209    pub fn digest(&self) -> [u8; 32] {
210        Self::compute_digest_for_modules_and_deps(
211            self.module_map.values(),
212            self.linkage_table
213                .values()
214                .map(|UpgradeInfo { upgraded_id, .. }| upgraded_id),
215        )
216    }
217
218    /// It is important that this function is shared across both the calculation
219    /// of the digest for the package, and the calculation of the digest
220    /// on-chain.
221    pub fn compute_digest_for_modules_and_deps<'a>(
222        modules: impl IntoIterator<Item = &'a Vec<u8>>,
223        object_ids: impl IntoIterator<Item = &'a ObjectID>,
224    ) -> [u8; 32] {
225        let mut components = object_ids
226            .into_iter()
227            .map(|o| ***o)
228            .chain(
229                modules
230                    .into_iter()
231                    .map(|module| DefaultHash::digest(module).digest),
232            )
233            .collect::<Vec<_>>();
234
235        // NB: sorting so the order of the modules and the order of the dependencies
236        // does not matter.
237        components.sort();
238
239        let mut digest = DefaultHash::default();
240        for c in components {
241            digest.update(c);
242        }
243        digest.finalize().digest
244    }
245
246    /// Create an initial version of the package along with this version's type
247    /// origin and linkage tables.
248    pub fn new_initial<'p>(
249        modules: &[CompiledModule],
250        protocol_config: &ProtocolConfig,
251        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
252    ) -> Result<Self, ExecutionError> {
253        let module = modules
254            .first()
255            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
256        let runtime_id = ObjectID::from(*module.address());
257        let storage_id = runtime_id;
258        let type_origin_table = build_initial_type_origin_table(modules);
259        Self::from_module_iter_with_type_origin_table(
260            storage_id,
261            runtime_id,
262            OBJECT_START_VERSION,
263            modules,
264            protocol_config,
265            type_origin_table,
266            transitive_dependencies,
267        )
268    }
269
270    /// Create an upgraded version of the package along with this version's type
271    /// origin and linkage tables.
272    pub fn new_upgraded<'p>(
273        &self,
274        storage_id: ObjectID,
275        modules: &[CompiledModule],
276        protocol_config: &ProtocolConfig,
277        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
278    ) -> Result<Self, ExecutionError> {
279        let module = modules
280            .first()
281            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
282        let runtime_id = ObjectID::from(*module.address());
283        let type_origin_table = build_upgraded_type_origin_table(self, modules, storage_id)?;
284        let mut new_version = self.version();
285        new_version.increment();
286        Self::from_module_iter_with_type_origin_table(
287            storage_id,
288            runtime_id,
289            new_version,
290            modules,
291            protocol_config,
292            type_origin_table,
293            transitive_dependencies,
294        )
295    }
296
297    pub fn new_system(
298        version: SequenceNumber,
299        modules: &[CompiledModule],
300        dependencies: impl IntoIterator<Item = ObjectID>,
301    ) -> Self {
302        let module = modules
303            .first()
304            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
305
306        let storage_id = ObjectID::from(*module.address());
307        let type_origin_table = build_initial_type_origin_table(modules);
308
309        let linkage_table = BTreeMap::from_iter(dependencies.into_iter().map(|dep| {
310            let info = UpgradeInfo {
311                upgraded_id: dep,
312                // The upgraded version is used by other packages that transitively depend on this
313                // system package, to make sure that if they choose a different version to depend on
314                // compared to their dependencies, they pick a greater version.
315                //
316                // However, in the case of system packages, although they can be upgraded, unlike
317                // other packages, only one version can be in use on the network at any given time,
318                // so it is not possible for a package to require a different system package version
319                // compared to its dependencies.
320                //
321                // This reason, coupled with the fact that system packages can only depend on each
322                // other, mean that their own linkage tables always report a version of zero.
323                upgraded_version: SequenceNumber::new(),
324            };
325            (dep, info)
326        }));
327
328        let module_map = BTreeMap::from_iter(modules.iter().map(|module| {
329            let name = module.name().to_string();
330            let mut bytes = Vec::new();
331            module
332                .serialize_with_version(module.version, &mut bytes)
333                .unwrap();
334            (name, bytes)
335        }));
336
337        Self::new(
338            storage_id,
339            version,
340            module_map,
341            u64::MAX, // System packages are not subject to the size limit
342            type_origin_table,
343            linkage_table,
344        )
345        .expect("System packages are not subject to a size limit")
346    }
347
348    fn from_module_iter_with_type_origin_table<'p>(
349        storage_id: ObjectID,
350        self_id: ObjectID,
351        version: SequenceNumber,
352        modules: &[CompiledModule],
353        protocol_config: &ProtocolConfig,
354        type_origin_table: Vec<TypeOrigin>,
355        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
356    ) -> Result<Self, ExecutionError> {
357        let mut module_map = BTreeMap::new();
358        let mut immediate_dependencies = BTreeSet::new();
359
360        for module in modules {
361            let name = module.name().to_string();
362
363            immediate_dependencies.extend(
364                module
365                    .immediate_dependencies()
366                    .into_iter()
367                    .map(|dep| ObjectID::from(*dep.address())),
368            );
369
370            let mut bytes = Vec::new();
371            let version = if protocol_config.move_binary_format_version() > VERSION_6 {
372                module.version
373            } else {
374                VERSION_6
375            };
376            module.serialize_with_version(version, &mut bytes).unwrap();
377            module_map.insert(name, bytes);
378        }
379
380        immediate_dependencies.remove(&self_id);
381        let linkage_table = build_linkage_table(
382            immediate_dependencies,
383            transitive_dependencies,
384            protocol_config,
385        )?;
386        Self::new(
387            storage_id,
388            version,
389            module_map,
390            protocol_config.max_move_package_size(),
391            type_origin_table,
392            linkage_table,
393        )
394    }
395
396    // Retrieve the module with `ModuleId` in the given package.
397    // The module must be the `storage_id` or the call will return `None`.
398    // Check if the address of the module is the same of the package
399    // and return `None` if that is not the case.
400    // All modules in a package share the address with the package.
401    pub fn get_module(&self, storage_id: &ModuleId) -> Option<&Vec<u8>> {
402        if self.id != ObjectID::from(*storage_id.address()) {
403            None
404        } else {
405            self.module_map.get(&storage_id.name().to_string())
406        }
407    }
408
409    /// Return the size of the package in bytes
410    pub fn size(&self) -> usize {
411        let module_map_size = self
412            .module_map
413            .iter()
414            .map(|(name, module)| name.len() + module.len())
415            .sum::<usize>();
416        let type_origin_table_size = self
417            .type_origin_table
418            .iter()
419            .map(
420                |TypeOrigin {
421                     module_name,
422                     datatype_name: struct_name,
423                     ..
424                 }| module_name.len() + struct_name.len() + ObjectID::LENGTH,
425            )
426            .sum::<usize>();
427
428        let linkage_table_size = self.linkage_table.len()
429            * (ObjectID::LENGTH
430                + (
431                    ObjectID::LENGTH + 8
432                    // SequenceNumber
433                ));
434
435        8 /* SequenceNumber */ + module_map_size + type_origin_table_size + linkage_table_size
436    }
437
438    pub fn id(&self) -> ObjectID {
439        self.id
440    }
441
442    pub fn version(&self) -> SequenceNumber {
443        self.version
444    }
445
446    pub fn decrement_version(&mut self) {
447        self.version.decrement();
448    }
449
450    pub fn increment_version(&mut self) {
451        self.version.increment();
452    }
453
454    /// Approximate size of the package in bytes. This is used for gas metering.
455    pub fn object_size_for_gas_metering(&self) -> usize {
456        self.size()
457    }
458
459    pub fn serialized_module_map(&self) -> &BTreeMap<String, Vec<u8>> {
460        &self.module_map
461    }
462
463    pub fn type_origin_table(&self) -> &Vec<TypeOrigin> {
464        &self.type_origin_table
465    }
466
467    pub fn type_origin_map(&self) -> BTreeMap<(String, String), ObjectID> {
468        self.type_origin_table
469            .iter()
470            .map(
471                |TypeOrigin {
472                     module_name,
473                     datatype_name: struct_name,
474                     package,
475                 }| { ((module_name.clone(), struct_name.clone()), *package) },
476            )
477            .collect()
478    }
479
480    pub fn linkage_table(&self) -> &BTreeMap<ObjectID, UpgradeInfo> {
481        &self.linkage_table
482    }
483
484    /// The ObjectID that this package's modules believe they are from, at
485    /// runtime (can differ from `MovePackage::id()` in the case of package
486    /// upgrades).
487    pub fn original_package_id(&self) -> ObjectID {
488        if self.version == OBJECT_START_VERSION {
489            // for a non-upgraded package, original ID is just the package ID
490            return self.id;
491        }
492
493        let bytes = self.module_map.values().next().expect("Empty module map");
494        let module = CompiledModule::deserialize_with_defaults(bytes)
495            .expect("A Move package contains a module that cannot be deserialized");
496        (*module.address()).into()
497    }
498
499    pub fn deserialize_module(
500        &self,
501        module: &Identifier,
502        binary_config: &BinaryConfig,
503    ) -> IotaResult<CompiledModule> {
504        // TODO use the session's cache
505        let bytes = self
506            .serialized_module_map()
507            .get(module.as_str())
508            .ok_or_else(|| IotaError::ModuleNotFound {
509                module_name: module.to_string(),
510            })?;
511        CompiledModule::deserialize_with_config(bytes, binary_config).map_err(|error| {
512            IotaError::ModuleDeserializationFailure {
513                error: error.to_string(),
514            }
515        })
516    }
517    /// If `include_code` is set to `false`, the normalized module will skip
518    /// function bodies but still include the signatures.
519    pub fn normalize<S: Hash + Eq + Clone + ToString, Pool: normalized::StringPool<String = S>>(
520        &self,
521        pool: &mut Pool,
522        binary_config: &BinaryConfig,
523        include_code: bool,
524    ) -> IotaResult<BTreeMap<String, normalized::Module<S>>> {
525        normalize_modules(pool, self.module_map.values(), binary_config, include_code)
526    }
527}
528
529impl UpgradeCap {
530    pub fn type_() -> StructTag {
531        StructTag {
532            address: IOTA_FRAMEWORK_ADDRESS,
533            module: PACKAGE_MODULE_NAME.to_owned(),
534            name: UPGRADECAP_STRUCT_NAME.to_owned(),
535            type_params: vec![],
536        }
537    }
538
539    /// Create an `UpgradeCap` for the newly published package at `package_id`,
540    /// and associate it with the fresh `uid`.
541    pub fn new(uid: ObjectID, package_id: ObjectID) -> Self {
542        UpgradeCap {
543            id: UID::new(uid),
544            package: ID::new(package_id),
545            version: 1,
546            policy: UpgradePolicy::COMPATIBLE,
547        }
548    }
549}
550
551impl UpgradeTicket {
552    pub fn type_() -> StructTag {
553        StructTag {
554            address: IOTA_FRAMEWORK_ADDRESS,
555            module: PACKAGE_MODULE_NAME.to_owned(),
556            name: UPGRADETICKET_STRUCT_NAME.to_owned(),
557            type_params: vec![],
558        }
559    }
560}
561
562impl UpgradeReceipt {
563    pub fn type_() -> StructTag {
564        StructTag {
565            address: IOTA_FRAMEWORK_ADDRESS,
566            module: PACKAGE_MODULE_NAME.to_owned(),
567            name: UPGRADERECEIPT_STRUCT_NAME.to_owned(),
568            type_params: vec![],
569        }
570    }
571
572    /// Create an `UpgradeReceipt` for the upgraded package at `package_id`
573    /// using the `UpgradeTicket` and newly published package id.
574    pub fn new(upgrade_ticket: UpgradeTicket, upgraded_package_id: ObjectID) -> Self {
575        UpgradeReceipt {
576            cap: upgrade_ticket.cap,
577            package: ID::new(upgraded_package_id),
578        }
579    }
580}
581
582/// Checks if a function is annotated with one of the test-related annotations
583pub fn is_test_fun(name: &IdentStr, module: &CompiledModule, fn_info_map: &FnInfoMap) -> bool {
584    let fn_name = name.to_string();
585    let mod_handle = module.self_handle();
586    let mod_addr = *module.address_identifier_at(mod_handle.address);
587    let fn_info_key = FnInfoKey { fn_name, mod_addr };
588    match fn_info_map.get(&fn_info_key) {
589        Some(fn_info) => fn_info.is_test,
590        None => false,
591    }
592}
593
594/// If `include_code` is set to `false`, the normalized module will skip
595/// function bodies but still include the signatures.
596pub fn normalize_modules<
597    'a,
598    S: Hash + Eq + Clone + ToString,
599    Pool: normalized::StringPool<String = S>,
600    I,
601>(
602    pool: &mut Pool,
603    modules: I,
604    binary_config: &BinaryConfig,
605    include_code: bool,
606) -> IotaResult<BTreeMap<String, normalized::Module<S>>>
607where
608    I: Iterator<Item = &'a Vec<u8>>,
609{
610    let mut normalized_modules = BTreeMap::new();
611    for bytecode in modules {
612        let module =
613            CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
614                IotaError::ModuleDeserializationFailure {
615                    error: error.to_string(),
616                }
617            })?;
618        let normalized_module = normalized::Module::new(pool, &module, include_code);
619        normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
620    }
621    Ok(normalized_modules)
622}
623
624/// If `include_code` is set to `false`, the normalized module will skip
625/// function bodies but still include the signatures.
626pub fn normalize_deserialized_modules<
627    'a,
628    S: Hash + Eq + Clone + ToString,
629    Pool: normalized::StringPool<String = S>,
630    I,
631>(
632    pool: &mut Pool,
633    modules: I,
634    include_code: bool,
635) -> BTreeMap<String, normalized::Module<S>>
636where
637    I: Iterator<Item = &'a CompiledModule>,
638{
639    let mut normalized_modules = BTreeMap::new();
640    for module in modules {
641        let normalized_module = normalized::Module::new(pool, module, include_code);
642        normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
643    }
644    normalized_modules
645}
646
647fn build_linkage_table<'p>(
648    mut immediate_dependencies: BTreeSet<ObjectID>,
649    transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
650    protocol_config: &ProtocolConfig,
651) -> Result<BTreeMap<ObjectID, UpgradeInfo>, ExecutionError> {
652    let mut linkage_table = BTreeMap::new();
653    let mut dep_linkage_tables = vec![];
654
655    for transitive_dep in transitive_dependencies.into_iter() {
656        // original_package_id will deserialize a module but only for the purpose of
657        // obtaining "original ID" of the package containing it so using max
658        // Move binary version during deserialization is OK
659        let original_id = transitive_dep.original_package_id();
660
661        let imm_dep = immediate_dependencies.remove(&original_id);
662
663        if protocol_config.dependency_linkage_error() {
664            dep_linkage_tables.push(&transitive_dep.linkage_table);
665
666            let existing = linkage_table.insert(
667                original_id,
668                UpgradeInfo {
669                    upgraded_id: transitive_dep.id,
670                    upgraded_version: transitive_dep.version,
671                },
672            );
673
674            if existing.is_some() {
675                return Err(ExecutionErrorKind::InvalidLinkage.into());
676            }
677        } else {
678            if imm_dep {
679                // Found an immediate dependency, mark it as seen, and stash a reference to its
680                // linkage table to check later.
681                dep_linkage_tables.push(&transitive_dep.linkage_table);
682            }
683            linkage_table.insert(
684                original_id,
685                UpgradeInfo {
686                    upgraded_id: transitive_dep.id,
687                    upgraded_version: transitive_dep.version,
688                },
689            );
690        }
691    }
692    // (1) Every dependency is represented in the transitive dependencies
693    if !immediate_dependencies.is_empty() {
694        return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
695    }
696
697    // (2) Every dependency's linkage table is superseded by this linkage table
698    for dep_linkage_table in dep_linkage_tables {
699        for (original_id, dep_info) in dep_linkage_table {
700            let Some(our_info) = linkage_table.get(original_id) else {
701                return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
702            };
703
704            if our_info.upgraded_version < dep_info.upgraded_version {
705                return Err(ExecutionErrorKind::PublishUpgradeDependencyDowngrade.into());
706            }
707        }
708    }
709
710    Ok(linkage_table)
711}
712
713fn build_initial_type_origin_table(modules: &[CompiledModule]) -> Vec<TypeOrigin> {
714    modules
715        .iter()
716        .flat_map(|m| {
717            m.struct_defs()
718                .iter()
719                .map(|struct_def| {
720                    let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
721                    let module_name = m.name().to_string();
722                    let struct_name = m.identifier_at(struct_handle.name).to_string();
723                    let package: ObjectID = (*m.self_id().address()).into();
724                    TypeOrigin {
725                        module_name,
726                        datatype_name: struct_name,
727                        package,
728                    }
729                })
730                .chain(m.enum_defs().iter().map(|enum_def| {
731                    let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
732                    let module_name = m.name().to_string();
733                    let enum_name = m.identifier_at(enum_handle.name).to_string();
734                    let package: ObjectID = (*m.self_id().address()).into();
735                    TypeOrigin {
736                        module_name,
737                        datatype_name: enum_name,
738                        package,
739                    }
740                }))
741        })
742        .collect()
743}
744
745fn build_upgraded_type_origin_table(
746    predecessor: &MovePackage,
747    modules: &[CompiledModule],
748    storage_id: ObjectID,
749) -> Result<Vec<TypeOrigin>, ExecutionError> {
750    let mut new_table = vec![];
751    let mut existing_table = predecessor.type_origin_map();
752    for m in modules {
753        for struct_def in m.struct_defs() {
754            let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
755            let module_name = m.name().to_string();
756            let struct_name = m.identifier_at(struct_handle.name).to_string();
757            let mod_key = (module_name.clone(), struct_name.clone());
758            // if id exists in the predecessor's table, use it, otherwise use the id of the
759            // upgraded module
760            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
761            new_table.push(TypeOrigin {
762                module_name,
763                datatype_name: struct_name,
764                package,
765            });
766        }
767
768        for enum_def in m.enum_defs() {
769            let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
770            let module_name = m.name().to_string();
771            let enum_name = m.identifier_at(enum_handle.name).to_string();
772            let mod_key = (module_name.clone(), enum_name.clone());
773            // if id exists in the predecessor's table, use it, otherwise use the id of the
774            // upgraded module
775            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
776            new_table.push(TypeOrigin {
777                module_name,
778                datatype_name: enum_name,
779                package,
780            });
781        }
782    }
783
784    if !existing_table.is_empty() {
785        Err(ExecutionError::from_kind(
786            ExecutionErrorKind::PackageUpgradeError {
787                upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
788            },
789        ))
790    } else {
791        Ok(new_table)
792    }
793}