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 move_disassembler::disassembler::Disassembler;
21use move_ir_types::location::Spanned;
22use schemars::JsonSchema;
23use serde::{Deserialize, Serialize};
24use serde_json::Value;
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        max_move_package_size: u64,
251        move_binary_format_version: u32,
252        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
253    ) -> Result<Self, ExecutionError> {
254        let module = modules
255            .first()
256            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
257        let runtime_id = ObjectID::from(*module.address());
258        let storage_id = runtime_id;
259        let type_origin_table = build_initial_type_origin_table(modules);
260        Self::from_module_iter_with_type_origin_table(
261            storage_id,
262            runtime_id,
263            OBJECT_START_VERSION,
264            modules,
265            max_move_package_size,
266            move_binary_format_version,
267            type_origin_table,
268            transitive_dependencies,
269        )
270    }
271
272    /// Create an upgraded version of the package along with this version's type
273    /// origin and linkage tables.
274    pub fn new_upgraded<'p>(
275        &self,
276        storage_id: ObjectID,
277        modules: &[CompiledModule],
278        protocol_config: &ProtocolConfig,
279        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
280    ) -> Result<Self, ExecutionError> {
281        let module = modules
282            .first()
283            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
284        let runtime_id = ObjectID::from(*module.address());
285        let type_origin_table = build_upgraded_type_origin_table(self, modules, storage_id)?;
286        let mut new_version = self.version();
287        new_version.increment();
288        Self::from_module_iter_with_type_origin_table(
289            storage_id,
290            runtime_id,
291            new_version,
292            modules,
293            protocol_config.max_move_package_size(),
294            protocol_config.move_binary_format_version(),
295            type_origin_table,
296            transitive_dependencies,
297        )
298    }
299
300    pub fn new_system(
301        version: SequenceNumber,
302        modules: &[CompiledModule],
303        dependencies: impl IntoIterator<Item = ObjectID>,
304    ) -> Self {
305        let module = modules
306            .first()
307            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
308
309        let storage_id = ObjectID::from(*module.address());
310        let type_origin_table = build_initial_type_origin_table(modules);
311
312        let linkage_table = BTreeMap::from_iter(dependencies.into_iter().map(|dep| {
313            let info = UpgradeInfo {
314                upgraded_id: dep,
315                // The upgraded version is used by other packages that transitively depend on this
316                // system package, to make sure that if they choose a different version to depend on
317                // compared to their dependencies, they pick a greater version.
318                //
319                // However, in the case of system packages, although they can be upgraded, unlike
320                // other packages, only one version can be in use on the network at any given time,
321                // so it is not possible for a package to require a different system package version
322                // compared to its dependencies.
323                //
324                // This reason, coupled with the fact that system packages can only depend on each
325                // other, mean that their own linkage tables always report a version of zero.
326                upgraded_version: SequenceNumber::new(),
327            };
328            (dep, info)
329        }));
330
331        let module_map = BTreeMap::from_iter(modules.iter().map(|module| {
332            let name = module.name().to_string();
333            let mut bytes = Vec::new();
334            module
335                .serialize_with_version(module.version, &mut bytes)
336                .unwrap();
337            (name, bytes)
338        }));
339
340        Self::new(
341            storage_id,
342            version,
343            module_map,
344            u64::MAX, // System packages are not subject to the size limit
345            type_origin_table,
346            linkage_table,
347        )
348        .expect("System packages are not subject to a size limit")
349    }
350
351    fn from_module_iter_with_type_origin_table<'p>(
352        storage_id: ObjectID,
353        self_id: ObjectID,
354        version: SequenceNumber,
355        modules: &[CompiledModule],
356        max_move_package_size: u64,
357        move_binary_format_version: u32,
358        type_origin_table: Vec<TypeOrigin>,
359        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
360    ) -> Result<Self, ExecutionError> {
361        let mut module_map = BTreeMap::new();
362        let mut immediate_dependencies = BTreeSet::new();
363
364        for module in modules {
365            let name = module.name().to_string();
366
367            immediate_dependencies.extend(
368                module
369                    .immediate_dependencies()
370                    .into_iter()
371                    .map(|dep| ObjectID::from(*dep.address())),
372            );
373
374            let mut bytes = Vec::new();
375            let version = if move_binary_format_version > VERSION_6 {
376                module.version
377            } else {
378                VERSION_6
379            };
380            module.serialize_with_version(version, &mut bytes).unwrap();
381            module_map.insert(name, bytes);
382        }
383
384        immediate_dependencies.remove(&self_id);
385        let linkage_table = build_linkage_table(immediate_dependencies, transitive_dependencies)?;
386        Self::new(
387            storage_id,
388            version,
389            module_map,
390            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
518    pub fn disassemble(&self) -> IotaResult<BTreeMap<String, Value>> {
519        disassemble_modules(self.module_map.values())
520    }
521
522    pub fn normalize(
523        &self,
524        binary_config: &BinaryConfig,
525    ) -> IotaResult<BTreeMap<String, normalized::Module>> {
526        normalize_modules(self.module_map.values(), binary_config)
527    }
528}
529
530impl UpgradeCap {
531    pub fn type_() -> StructTag {
532        StructTag {
533            address: IOTA_FRAMEWORK_ADDRESS,
534            module: PACKAGE_MODULE_NAME.to_owned(),
535            name: UPGRADECAP_STRUCT_NAME.to_owned(),
536            type_params: vec![],
537        }
538    }
539
540    /// Create an `UpgradeCap` for the newly published package at `package_id`,
541    /// and associate it with the fresh `uid`.
542    pub fn new(uid: ObjectID, package_id: ObjectID) -> Self {
543        UpgradeCap {
544            id: UID::new(uid),
545            package: ID::new(package_id),
546            version: 1,
547            policy: UpgradePolicy::COMPATIBLE,
548        }
549    }
550}
551
552impl UpgradeTicket {
553    pub fn type_() -> StructTag {
554        StructTag {
555            address: IOTA_FRAMEWORK_ADDRESS,
556            module: PACKAGE_MODULE_NAME.to_owned(),
557            name: UPGRADETICKET_STRUCT_NAME.to_owned(),
558            type_params: vec![],
559        }
560    }
561}
562
563impl UpgradeReceipt {
564    pub fn type_() -> StructTag {
565        StructTag {
566            address: IOTA_FRAMEWORK_ADDRESS,
567            module: PACKAGE_MODULE_NAME.to_owned(),
568            name: UPGRADERECEIPT_STRUCT_NAME.to_owned(),
569            type_params: vec![],
570        }
571    }
572
573    /// Create an `UpgradeReceipt` for the upgraded package at `package_id`
574    /// using the `UpgradeTicket` and newly published package id.
575    pub fn new(upgrade_ticket: UpgradeTicket, upgraded_package_id: ObjectID) -> Self {
576        UpgradeReceipt {
577            cap: upgrade_ticket.cap,
578            package: ID::new(upgraded_package_id),
579        }
580    }
581}
582
583/// Checks if a function is annotated with one of the test-related annotations
584pub fn is_test_fun(name: &IdentStr, module: &CompiledModule, fn_info_map: &FnInfoMap) -> bool {
585    let fn_name = name.to_string();
586    let mod_handle = module.self_handle();
587    let mod_addr = *module.address_identifier_at(mod_handle.address);
588    let fn_info_key = FnInfoKey { fn_name, mod_addr };
589    match fn_info_map.get(&fn_info_key) {
590        Some(fn_info) => fn_info.is_test,
591        None => false,
592    }
593}
594
595pub fn disassemble_modules<'a, I>(modules: I) -> IotaResult<BTreeMap<String, Value>>
596where
597    I: Iterator<Item = &'a Vec<u8>>,
598{
599    let mut disassembled = BTreeMap::new();
600    for bytecode in modules {
601        // this function is only from JSON RPC - it is OK to deserialize with max Move
602        // binary version
603        let module = CompiledModule::deserialize_with_defaults(bytecode).map_err(|error| {
604            IotaError::ModuleDeserializationFailure {
605                error: error.to_string(),
606            }
607        })?;
608        let d =
609            Disassembler::from_module(&module, Spanned::unsafe_no_loc(()).loc).map_err(|e| {
610                IotaError::ObjectSerialization {
611                    error: e.to_string(),
612                }
613            })?;
614        let bytecode_str = d
615            .disassemble()
616            .map_err(|e| IotaError::ObjectSerialization {
617                error: e.to_string(),
618            })?;
619        disassembled.insert(module.name().to_string(), Value::String(bytecode_str));
620    }
621    Ok(disassembled)
622}
623
624pub fn normalize_modules<'a, I>(
625    modules: I,
626    binary_config: &BinaryConfig,
627) -> IotaResult<BTreeMap<String, normalized::Module>>
628where
629    I: Iterator<Item = &'a Vec<u8>>,
630{
631    let mut normalized_modules = BTreeMap::new();
632    for bytecode in modules {
633        let module =
634            CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
635                IotaError::ModuleDeserializationFailure {
636                    error: error.to_string(),
637                }
638            })?;
639        let normalized_module = normalized::Module::new(&module);
640        normalized_modules.insert(normalized_module.name.to_string(), normalized_module);
641    }
642    Ok(normalized_modules)
643}
644
645pub fn normalize_deserialized_modules<'a, I>(modules: I) -> BTreeMap<String, normalized::Module>
646where
647    I: Iterator<Item = &'a CompiledModule>,
648{
649    let mut normalized_modules = BTreeMap::new();
650    for module in modules {
651        let normalized_module = normalized::Module::new(module);
652        normalized_modules.insert(normalized_module.name.to_string(), normalized_module);
653    }
654    normalized_modules
655}
656
657fn build_linkage_table<'p>(
658    mut immediate_dependencies: BTreeSet<ObjectID>,
659    transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
660) -> Result<BTreeMap<ObjectID, UpgradeInfo>, ExecutionError> {
661    let mut linkage_table = BTreeMap::new();
662    let mut dep_linkage_tables = vec![];
663
664    for transitive_dep in transitive_dependencies.into_iter() {
665        // original_package_id will deserialize a module but only for the purpose of
666        // obtaining "original ID" of the package containing it so using max
667        // Move binary version during deserialization is OK
668        let original_id = transitive_dep.original_package_id();
669
670        if immediate_dependencies.remove(&original_id) {
671            // Found an immediate dependency, mark it as seen, and stash a reference to its
672            // linkage table to check later.
673            dep_linkage_tables.push(&transitive_dep.linkage_table);
674        }
675
676        linkage_table.insert(
677            original_id,
678            UpgradeInfo {
679                upgraded_id: transitive_dep.id,
680                upgraded_version: transitive_dep.version,
681            },
682        );
683    }
684    // (1) Every dependency is represented in the transitive dependencies
685    if !immediate_dependencies.is_empty() {
686        return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
687    }
688
689    // (2) Every dependency's linkage table is superseded by this linkage table
690    for dep_linkage_table in dep_linkage_tables {
691        for (original_id, dep_info) in dep_linkage_table {
692            let Some(our_info) = linkage_table.get(original_id) else {
693                return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
694            };
695
696            if our_info.upgraded_version < dep_info.upgraded_version {
697                return Err(ExecutionErrorKind::PublishUpgradeDependencyDowngrade.into());
698            }
699        }
700    }
701
702    Ok(linkage_table)
703}
704
705fn build_initial_type_origin_table(modules: &[CompiledModule]) -> Vec<TypeOrigin> {
706    modules
707        .iter()
708        .flat_map(|m| {
709            m.struct_defs()
710                .iter()
711                .map(|struct_def| {
712                    let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
713                    let module_name = m.name().to_string();
714                    let struct_name = m.identifier_at(struct_handle.name).to_string();
715                    let package: ObjectID = (*m.self_id().address()).into();
716                    TypeOrigin {
717                        module_name,
718                        datatype_name: struct_name,
719                        package,
720                    }
721                })
722                .chain(m.enum_defs().iter().map(|enum_def| {
723                    let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
724                    let module_name = m.name().to_string();
725                    let enum_name = m.identifier_at(enum_handle.name).to_string();
726                    let package: ObjectID = (*m.self_id().address()).into();
727                    TypeOrigin {
728                        module_name,
729                        datatype_name: enum_name,
730                        package,
731                    }
732                }))
733        })
734        .collect()
735}
736
737fn build_upgraded_type_origin_table(
738    predecessor: &MovePackage,
739    modules: &[CompiledModule],
740    storage_id: ObjectID,
741) -> Result<Vec<TypeOrigin>, ExecutionError> {
742    let mut new_table = vec![];
743    let mut existing_table = predecessor.type_origin_map();
744    for m in modules {
745        for struct_def in m.struct_defs() {
746            let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
747            let module_name = m.name().to_string();
748            let struct_name = m.identifier_at(struct_handle.name).to_string();
749            let mod_key = (module_name.clone(), struct_name.clone());
750            // if id exists in the predecessor's table, use it, otherwise use the id of the
751            // upgraded module
752            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
753            new_table.push(TypeOrigin {
754                module_name,
755                datatype_name: struct_name,
756                package,
757            });
758        }
759
760        for enum_def in m.enum_defs() {
761            let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
762            let module_name = m.name().to_string();
763            let enum_name = m.identifier_at(enum_handle.name).to_string();
764            let mod_key = (module_name.clone(), enum_name.clone());
765            // if id exists in the predecessor's table, use it, otherwise use the id of the
766            // upgraded module
767            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
768            new_table.push(TypeOrigin {
769                module_name,
770                datatype_name: enum_name,
771                package,
772            });
773        }
774    }
775
776    if !existing_table.is_empty() {
777        Err(ExecutionError::from_kind(
778            ExecutionErrorKind::PackageUpgradeError {
779                upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
780            },
781        ))
782    } else {
783        Ok(new_table)
784    }
785}