sui_types/
move_package.rs

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