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