Skip to main content

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
5//! Move package.
6//!
7//! This module contains the [MovePackage] and types necessary for describing
8//! its update behavior and linkage information for module resolution during
9//! execution.
10//!
11//! Upgradeable packages form a version chain. This is simply the conceptual
12//! chain of package versions, with their monotonically increasing version
13//! numbers. Package { version: 1 } => Package { version: 2 } => ...
14//!
15//! The code contains terminology that may be confusing for the uninitiated,
16//! like `Module ID`, `Package ID`, `Storage ID` and `Runtime ID`. For avoidance
17//! of doubt these concepts are defined like so:
18//! - `Package ID` is the [ObjectId] representing the address by which the given
19//!   package may be found in storage.
20//! - `Runtime ID` will always mean the `Package ID`/`Storage ID` of the
21//!   initially published package. For a non upgradeable package this will
22//!   always be equal to `Storage ID`. For an upgradeable package, it will be
23//!   the `Storage ID` of the package's first deployed version.
24//! - `Storage ID` is the `Package ID`, and it is mostly used in to highlight
25//!   that we are talking about the current `Package ID` and not the `Runtime
26//!   ID`
27//! - `Module ID` is the the type
28//!   [ModuleID](move_core_types::language_storage::ModuleId).
29//!
30//! Some of these are redundant and have overlapping meaning, so whenever
31//! reasonable/necessary the possible naming will be listed. From all of these
32//! `Runtime ID` and `Module ID` are the most confusing. `Module ID` may be used
33//! with `Runtime ID` and `Storage ID` depending on the context. While `Runtime
34//! ID` is mostly used in name resolution during runtime, when a package with
35//! its modules has been loaded.
36
37use std::{
38    collections::{BTreeMap, BTreeSet},
39    hash::Hash,
40};
41
42use derive_more::Display;
43use iota_protocol_config::ProtocolConfig;
44use iota_sdk_types::{
45    Identifier, ObjectId, PackageUpgradeError, StructTag, TypeTag, Version,
46    move_package::{MovePackage, TypeOrigin, UpgradeInfo},
47};
48use move_binary_format::{
49    binary_config::BinaryConfig,
50    file_format::CompiledModule,
51    file_format_common::{IOTA_METADATA_KEY, VERSION_6},
52    normalized,
53};
54use move_core_types::identifier::IdentStr;
55use serde::{Deserialize, Serialize};
56use serde_with::{Bytes, serde_as};
57
58use crate::{
59    Address,
60    collection_types::{Entry, VecMap},
61    derived_object,
62    error::{ExecutionError, ExecutionErrorKind, IotaError, IotaResult},
63    id::{ID, UID},
64    iota_sdk_types_conversions::identifier_core_to_sdk,
65    iota_serde::TypeName,
66};
67
68pub const PACKAGE_METADATA_MODULE_NAME: Identifier = Identifier::from_static("package_metadata");
69pub const PACKAGE_METADATA_V1_STRUCT_NAME: Identifier =
70    Identifier::from_static("PackageMetadataV1");
71pub const PACKAGE_METADATA_KEY_STRUCT_NAME: Identifier =
72    Identifier::from_static("PackageMetadataKey");
73
74#[derive(Clone, Debug)]
75/// Additional information about a function
76pub struct FnInfo {
77    /// If true, it's a function involved in testing (`[test]`, `[test_only]`,
78    /// `[expected_failure]`)
79    pub is_test: bool,
80    /// If set, function was marked to represent authenticator function of
81    /// given version.
82    pub authenticator_version: Option<u8>,
83    pub is_view: bool,
84}
85
86#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
87/// Uniquely identifies a function in a module
88pub struct FnInfoKey {
89    pub fn_name: String,
90    pub mod_name: String,
91    pub mod_addr: Address,
92}
93
94/// A map from function info keys to function info
95pub type FnInfoMap = BTreeMap<FnInfoKey, FnInfo>;
96
97// NB: do _not_ add `Serialize` or `Deserialize` to this enum. Convert to u8
98// first  or use the associated constants before storing in any serialization
99// setting.
100/// Rust representation of upgrade policy constants in `iota::package`.
101#[repr(u8)]
102#[derive(Display, Debug, Clone, Copy)]
103pub enum UpgradePolicy {
104    #[display("COMPATIBLE")]
105    Compatible = 0,
106    #[display("ADDITIVE")]
107    Additive = 128,
108    #[display("DEP_ONLY")]
109    DepOnly = 192,
110}
111
112impl UpgradePolicy {
113    /// Convenience accessors to the upgrade policies as u8s.
114    pub const COMPATIBLE: u8 = Self::Compatible as u8;
115    pub const ADDITIVE: u8 = Self::Additive as u8;
116    pub const DEP_ONLY: u8 = Self::DepOnly as u8;
117
118    pub fn is_valid_policy(policy: &u8) -> bool {
119        Self::try_from(*policy).is_ok()
120    }
121}
122
123impl TryFrom<u8> for UpgradePolicy {
124    type Error = ();
125    fn try_from(value: u8) -> Result<Self, Self::Error> {
126        match value {
127            x if x == Self::Compatible as u8 => Ok(Self::Compatible),
128            x if x == Self::Additive as u8 => Ok(Self::Additive),
129            x if x == Self::DepOnly as u8 => Ok(Self::DepOnly),
130            _ => Err(()),
131        }
132    }
133}
134
135/// Rust representation of `iota::package::UpgradeCap`.
136#[derive(Debug, Serialize, Deserialize)]
137pub struct UpgradeCap {
138    pub id: UID,
139    pub package: ID,
140    pub version: u64,
141    pub policy: u8,
142}
143
144/// Rust representation of `iota::package::UpgradeTicket`.
145#[derive(Debug, Serialize, Deserialize)]
146pub struct UpgradeTicket {
147    pub cap: ID,
148    pub package: ID,
149    pub policy: u8,
150    pub digest: Vec<u8>,
151}
152
153/// Rust representation of `iota::package::UpgradeReceipt`.
154#[derive(Debug, Serialize, Deserialize)]
155pub struct UpgradeReceipt {
156    pub cap: ID,
157    pub package: ID,
158}
159
160mod move_package_ext {
161    pub trait Sealed {}
162    impl Sealed for super::MovePackage {}
163}
164
165pub trait MovePackageExt: Sized + move_package_ext::Sealed {
166    fn new_initial<'p>(
167        modules: &[CompiledModule],
168        protocol_config: &ProtocolConfig,
169        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
170    ) -> Result<MovePackage, ExecutionError>;
171
172    fn new_upgraded<'p>(
173        &self,
174        storage_id: ObjectId,
175        modules: &[CompiledModule],
176        protocol_config: &ProtocolConfig,
177        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
178    ) -> Result<MovePackage, ExecutionError>;
179
180    fn new_system(
181        version: Version,
182        modules: &[CompiledModule],
183        dependencies: impl IntoIterator<Item = ObjectId>,
184    ) -> MovePackage;
185
186    fn from_module_iter_with_type_origin_table<'p>(
187        storage_id: ObjectId,
188        self_id: ObjectId,
189        version: Version,
190        modules: &[CompiledModule],
191        protocol_config: &ProtocolConfig,
192        type_origin_table: Vec<TypeOrigin>,
193        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
194    ) -> Result<MovePackage, ExecutionError>;
195
196    fn original_package_id(&self) -> ObjectId;
197
198    fn deserialize_module(
199        &self,
200        module: &Identifier,
201        binary_config: &BinaryConfig,
202    ) -> IotaResult<CompiledModule>;
203
204    fn normalize<S: Hash + Eq + Clone + ToString, Pool: normalized::StringPool<String = S>>(
205        &self,
206        pool: &mut Pool,
207        binary_config: &BinaryConfig,
208        include_code: bool,
209    ) -> IotaResult<BTreeMap<String, normalized::Module<S>>>;
210}
211
212impl MovePackageExt for MovePackage {
213    /// Create an initial version of the package along with this version's type
214    /// origin and linkage tables.
215    ///
216    /// # Undefined behavior
217    ///
218    /// All passed modules must have the same `Runtime ID` or the behavior is
219    /// undefined.
220    fn new_initial<'p>(
221        modules: &[CompiledModule],
222        protocol_config: &ProtocolConfig,
223        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
224    ) -> Result<MovePackage, ExecutionError> {
225        let module = modules
226            .first()
227            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
228        let runtime_id = ObjectId::new(module.address().into_bytes());
229        let storage_id = runtime_id;
230        let type_origin_table = build_initial_type_origin_table(modules);
231
232        MovePackage::from_module_iter_with_type_origin_table(
233            storage_id,
234            runtime_id,
235            Version::OBJECT_START,
236            modules,
237            protocol_config,
238            type_origin_table,
239            transitive_dependencies,
240        )
241    }
242
243    /// Create an upgraded version of the package along with this version's type
244    /// origin and linkage tables.
245    ///
246    /// # Undefined behavior
247    ///
248    /// All passed modules must have the same `Runtime ID` or the behavior is
249    /// undefined.
250    fn new_upgraded<'p>(
251        &self,
252        storage_id: ObjectId,
253        modules: &[CompiledModule],
254        protocol_config: &ProtocolConfig,
255        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
256    ) -> Result<MovePackage, ExecutionError> {
257        let module = modules
258            .first()
259            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
260        let runtime_id = ObjectId::new(module.address().into_bytes());
261        let type_origin_table = build_upgraded_type_origin_table(self, modules, storage_id)?;
262        let mut new_version = self.version();
263        new_version.increment().unwrap();
264
265        MovePackage::from_module_iter_with_type_origin_table(
266            storage_id,
267            runtime_id,
268            new_version,
269            modules,
270            protocol_config,
271            type_origin_table,
272            transitive_dependencies,
273        )
274    }
275
276    fn new_system(
277        version: Version,
278        modules: &[CompiledModule],
279        dependencies: impl IntoIterator<Item = ObjectId>,
280    ) -> MovePackage {
281        let module = modules
282            .first()
283            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
284
285        let storage_id = ObjectId::new(module.address().into_bytes());
286        let type_origin_table = build_initial_type_origin_table(modules);
287
288        let linkage_table = BTreeMap::from_iter(dependencies.into_iter().map(|dep| {
289            let info = UpgradeInfo {
290                upgraded_id: dep,
291                // The upgraded version is used by other packages that transitively depend on this
292                // system package, to make sure that if they choose a different version to depend on
293                // compared to their dependencies, they pick a greater version.
294                //
295                // However, in the case of system packages, although they can be upgraded, unlike
296                // other packages, only one version can be in use on the network at any given time,
297                // so it is not possible for a package to require a different system package version
298                // compared to its dependencies.
299                //
300                // This reason, coupled with the fact that system packages can only depend on each
301                // other, mean that their own linkage tables always report a version of zero.
302                upgraded_version: Version::default(),
303            };
304            (dep, info)
305        }));
306
307        let module_map = BTreeMap::from_iter(modules.iter().map(|module| {
308            let name = identifier_core_to_sdk(module.name());
309            let mut bytes = Vec::new();
310            module
311                .serialize_with_version(module.version, &mut bytes)
312                .unwrap();
313            (name, bytes)
314        }));
315
316        MovePackage::new(
317            storage_id,
318            version,
319            module_map,
320            u64::MAX, // System packages are not subject to the size limit
321            type_origin_table,
322            linkage_table,
323        )
324        .expect("System packages are not subject to a size limit")
325    }
326
327    fn from_module_iter_with_type_origin_table<'p>(
328        storage_id: ObjectId,
329        self_id: ObjectId,
330        version: Version,
331        modules: &[CompiledModule],
332        protocol_config: &ProtocolConfig,
333        type_origin_table: Vec<TypeOrigin>,
334        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
335    ) -> Result<MovePackage, ExecutionError> {
336        let mut module_map = BTreeMap::new();
337        let mut immediate_dependencies = BTreeSet::new();
338
339        for module in modules {
340            let name = identifier_core_to_sdk(module.name());
341
342            immediate_dependencies.extend(
343                module
344                    .immediate_dependencies()
345                    .into_iter()
346                    .map(|dep| ObjectId::new(dep.address().into_bytes())),
347            );
348
349            let mut bytes = Vec::new();
350            let version = if protocol_config.move_binary_format_version() > VERSION_6 {
351                module.version
352            } else {
353                VERSION_6
354            };
355            module.serialize_with_version(version, &mut bytes).unwrap();
356            module_map.insert(name, bytes);
357        }
358
359        immediate_dependencies.remove(&self_id);
360        let linkage_table = build_linkage_table(
361            immediate_dependencies,
362            transitive_dependencies,
363            protocol_config,
364        )?;
365
366        Ok(MovePackage::new(
367            storage_id,
368            version,
369            module_map,
370            protocol_config.max_move_package_size(),
371            type_origin_table,
372            linkage_table,
373        )?)
374    }
375
376    /// The `Package ID` of the first version of this package.
377    ///
378    /// Also referred to as `Runtime ID`.
379    ///
380    /// Regardless of which version of the package we are working with, this
381    /// function will always return the `Package ID`/`Storage ID` of the first
382    /// package version in the version chain.
383    fn original_package_id(&self) -> ObjectId {
384        if self.version == Version::OBJECT_START {
385            // for a non-upgraded package, original ID is just the package ID
386            return self.id;
387        }
388
389        let bytes = self.modules.values().next().expect("Empty module map");
390        // Remember, that all modules will contain the `Package ID` of the first
391        // deployed package. This is why taking any of them will produce the
392        // original package id.
393        let module = CompiledModule::deserialize_with_defaults(bytes)
394            .expect("A Move package contains a module that cannot be deserialized");
395        ObjectId::new(module.address().into_bytes())
396    }
397
398    fn deserialize_module(
399        &self,
400        module: &Identifier,
401        binary_config: &BinaryConfig,
402    ) -> IotaResult<CompiledModule> {
403        // TODO use the session's cache
404        let bytes =
405            self.serialized_module_map()
406                .get(module)
407                .ok_or_else(|| IotaError::ModuleNotFound {
408                    module_name: module.to_string(),
409                })?;
410
411        CompiledModule::deserialize_with_config(bytes, binary_config).map_err(|error| {
412            IotaError::ModuleDeserializationFailure {
413                error: error.to_string(),
414            }
415        })
416    }
417
418    /// If `include_code` is set to `false`, the normalized module will skip
419    /// function bodies but still include the signatures.
420    fn normalize<S: Hash + Eq + Clone + ToString, Pool: normalized::StringPool<String = S>>(
421        &self,
422        pool: &mut Pool,
423        binary_config: &BinaryConfig,
424        include_code: bool,
425    ) -> IotaResult<BTreeMap<String, normalized::Module<S>>> {
426        normalize_modules(pool, self.modules.values(), binary_config, include_code)
427    }
428}
429
430impl UpgradeCap {
431    /// Create an `UpgradeCap` for the newly published package at `package_id`,
432    /// and associate it with the fresh `uid`.
433    pub fn new(uid: ObjectId, package_id: ObjectId) -> Self {
434        UpgradeCap {
435            id: UID::new(uid),
436            package: ID::new(package_id),
437            version: 1,
438            policy: UpgradePolicy::COMPATIBLE,
439        }
440    }
441}
442
443impl UpgradeReceipt {
444    /// Create an `UpgradeReceipt` for the upgraded package at `package_id`
445    /// using the `UpgradeTicket` and newly published package id.
446    pub fn new(upgrade_ticket: UpgradeTicket, upgraded_package_id: ObjectId) -> Self {
447        UpgradeReceipt {
448            cap: upgrade_ticket.cap,
449            package: ID::new(upgraded_package_id),
450        }
451    }
452}
453
454/// Checks if a function is annotated with one of the test-related annotations
455pub fn is_test_fun(name: &str, module: &CompiledModule, fn_info_map: &FnInfoMap) -> bool {
456    let mod_handle = module.self_handle();
457    let mod_addr = Address::new(
458        module
459            .address_identifier_at(mod_handle.address)
460            .into_bytes(),
461    );
462    let mod_name = module.name().to_string();
463    let fn_info_key = FnInfoKey {
464        fn_name: name.to_string(),
465        mod_name,
466        mod_addr,
467    };
468    match fn_info_map.get(&fn_info_key) {
469        Some(fn_info) => fn_info.is_test,
470        None => false,
471    }
472}
473
474pub fn get_authenticator_version_from_fun(
475    name: &str,
476    module: &CompiledModule,
477    fn_info_map: &FnInfoMap,
478) -> Option<u8> {
479    let mod_handle = module.self_handle();
480    let mod_addr = Address::from(
481        module
482            .address_identifier_at(mod_handle.address)
483            .into_bytes(),
484    );
485    let mod_name = module.name().to_string();
486    let fn_info_key = FnInfoKey {
487        fn_name: name.to_string(),
488        mod_name,
489        mod_addr,
490    };
491    match fn_info_map.get(&fn_info_key) {
492        Some(FnInfo {
493            is_test: _,
494            authenticator_version: Some(v),
495            is_view: _,
496        }) => Some(*v),
497        _ => None,
498    }
499}
500
501/// Returns true if a function is marked as a view function.
502pub fn is_view_function_from_fn_info(
503    name: &IdentStr,
504    module: &CompiledModule,
505    fn_info_map: &FnInfoMap,
506) -> bool {
507    let fn_name = name.to_string();
508    let mod_handle = module.self_handle();
509    let mod_addr = Address::from(
510        module
511            .address_identifier_at(mod_handle.address)
512            .into_bytes(),
513    );
514    let mod_name = module.name().to_string();
515    let fn_info_key = FnInfoKey {
516        fn_name,
517        mod_name,
518        mod_addr,
519    };
520    fn_info_map
521        .get(&fn_info_key)
522        .map(|info| info.is_view)
523        .unwrap_or(false)
524}
525
526/// If `include_code` is set to `false`, the normalized module will skip
527/// function bodies but still include the signatures.
528pub fn normalize_modules<
529    'a,
530    S: Hash + Eq + Clone + ToString,
531    Pool: normalized::StringPool<String = S>,
532    I,
533>(
534    pool: &mut Pool,
535    modules: I,
536    binary_config: &BinaryConfig,
537    include_code: bool,
538) -> IotaResult<BTreeMap<String, normalized::Module<S>>>
539where
540    I: Iterator<Item = &'a Vec<u8>>,
541{
542    let mut normalized_modules = BTreeMap::new();
543    for bytecode in modules {
544        let module =
545            CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
546                IotaError::ModuleDeserializationFailure {
547                    error: error.to_string(),
548                }
549            })?;
550        let normalized_module = normalized::Module::new(pool, &module, include_code);
551        normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
552    }
553    Ok(normalized_modules)
554}
555
556/// If `include_code` is set to `false`, the normalized module will skip
557/// function bodies but still include the signatures.
558///
559/// The returned metadata is the IOTA-specific runtime metadata attached to the
560/// module, or the default empty metadata when the module has no IOTA metadata.
561pub fn normalize_modules_with_metadata<
562    'a,
563    S: Hash + Eq + Clone + ToString,
564    Pool: normalized::StringPool<String = S>,
565    I,
566>(
567    pool: &mut Pool,
568    modules: I,
569    binary_config: &BinaryConfig,
570    include_code: bool,
571    protocol_config: Option<&ProtocolConfig>,
572) -> IotaResult<BTreeMap<String, (normalized::Module<S>, RuntimeModuleMetadata)>>
573where
574    I: Iterator<Item = &'a Vec<u8>>,
575{
576    let mut normalized_modules = BTreeMap::new();
577    for bytecode in modules {
578        let module =
579            CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
580                IotaError::ModuleDeserializationFailure {
581                    error: error.to_string(),
582                }
583            })?;
584        let metadata = runtime_module_metadata(&module, protocol_config)?;
585        let normalized_module = normalized::Module::new(pool, &module, include_code);
586        normalized_modules.insert(
587            normalized_module.name().to_string(),
588            (normalized_module, metadata),
589        );
590    }
591    Ok(normalized_modules)
592}
593
594/// If `include_code` is set to `false`, the normalized module will skip
595/// function bodies but still include the signatures.
596pub fn normalize_deserialized_modules<
597    'a,
598    S: Hash + Eq + Clone + ToString,
599    Pool: normalized::StringPool<String = S>,
600    I,
601>(
602    pool: &mut Pool,
603    modules: I,
604    include_code: bool,
605) -> BTreeMap<String, normalized::Module<S>>
606where
607    I: Iterator<Item = &'a CompiledModule>,
608{
609    let mut normalized_modules = BTreeMap::new();
610    for module in modules {
611        let normalized_module = normalized::Module::new(pool, module, include_code);
612        normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
613    }
614    normalized_modules
615}
616
617/// If `include_code` is set to `false`, the normalized module will skip
618/// function bodies but still include the signatures.
619///
620/// The returned metadata is the IOTA-specific runtime metadata attached to the
621/// module, or the default empty metadata when the module has no IOTA metadata.
622pub fn normalize_deserialized_modules_with_metadata<
623    'a,
624    S: Hash + Eq + Clone + ToString,
625    Pool: normalized::StringPool<String = S>,
626    I,
627>(
628    pool: &mut Pool,
629    modules: I,
630    include_code: bool,
631    protocol_config: Option<&ProtocolConfig>,
632) -> IotaResult<BTreeMap<String, (normalized::Module<S>, RuntimeModuleMetadata)>>
633where
634    I: Iterator<Item = &'a CompiledModule>,
635{
636    let mut normalized_modules = BTreeMap::new();
637    for module in modules {
638        let metadata = runtime_module_metadata(module, protocol_config)?;
639        let normalized_module = normalized::Module::new(pool, module, include_code);
640        normalized_modules.insert(
641            normalized_module.name().to_string(),
642            (normalized_module, metadata),
643        );
644    }
645    Ok(normalized_modules)
646}
647
648fn runtime_module_metadata(
649    module: &CompiledModule,
650    protocol_config: Option<&ProtocolConfig>,
651) -> IotaResult<RuntimeModuleMetadata> {
652    let build_config = ProtocolBuildConfig::from(protocol_config);
653    let Some(metadata) = module
654        .metadata
655        .iter()
656        .find(|metadata| metadata.key == IOTA_METADATA_KEY)
657    else {
658        if build_config.allow_view_function {
659            return Ok(RuntimeModuleMetadata::v2());
660        } else {
661            return Ok(RuntimeModuleMetadata::v1());
662        }
663    };
664
665    let metadata_wrapper: RuntimeModuleMetadataWrapper =
666        bcs::from_bytes(&metadata.value).map_err(|error| {
667            IotaError::RuntimeModuleMetadataDeserialization {
668                error: error.to_string(),
669            }
670        })?;
671    metadata_wrapper.try_into_runtime_module_metadata(&build_config)
672}
673
674fn build_linkage_table<'p>(
675    mut immediate_dependencies: BTreeSet<ObjectId>,
676    transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
677    protocol_config: &ProtocolConfig,
678) -> Result<BTreeMap<ObjectId, UpgradeInfo>, ExecutionError> {
679    let mut linkage_table = BTreeMap::new();
680    let mut dep_linkage_tables = vec![];
681
682    for transitive_dep in transitive_dependencies.into_iter() {
683        // original_package_id will deserialize a module but only for the purpose of
684        // obtaining "original ID" of the package containing it so using max
685        // Move binary version during deserialization is OK
686        let original_id = MovePackage::original_package_id(transitive_dep);
687
688        let imm_dep = immediate_dependencies.remove(&original_id);
689
690        if protocol_config.dependency_linkage_error() {
691            dep_linkage_tables.push(&transitive_dep.linkage_table);
692
693            let existing = linkage_table.insert(
694                original_id,
695                UpgradeInfo {
696                    upgraded_id: transitive_dep.id,
697                    upgraded_version: transitive_dep.version,
698                },
699            );
700
701            if existing.is_some() {
702                return Err(ExecutionErrorKind::InvalidLinkage.into());
703            }
704        } else {
705            if imm_dep {
706                // Found an immediate dependency, mark it as seen, and stash a reference to its
707                // linkage table to check later.
708                dep_linkage_tables.push(&transitive_dep.linkage_table);
709            }
710            linkage_table.insert(
711                original_id,
712                UpgradeInfo {
713                    upgraded_id: transitive_dep.id,
714                    upgraded_version: transitive_dep.version,
715                },
716            );
717        }
718    }
719    // (1) Every dependency is represented in the transitive dependencies
720    if !immediate_dependencies.is_empty() {
721        return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
722    }
723
724    // (2) Every dependency's linkage table is superseded by this linkage table
725    for dep_linkage_table in dep_linkage_tables {
726        for (original_id, dep_info) in dep_linkage_table {
727            let Some(our_info) = linkage_table.get(original_id) else {
728                return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
729            };
730
731            if our_info.upgraded_version < dep_info.upgraded_version {
732                return Err(ExecutionErrorKind::PublishUpgradeDependencyDowngrade.into());
733            }
734        }
735    }
736
737    Ok(linkage_table)
738}
739
740fn build_initial_type_origin_table(modules: &[CompiledModule]) -> Vec<TypeOrigin> {
741    modules
742        .iter()
743        .flat_map(|m| {
744            m.struct_defs()
745                .iter()
746                .map(|struct_def| {
747                    let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
748                    let package = ObjectId::new(m.self_id().address().into_bytes());
749                    TypeOrigin {
750                        module_name: identifier_core_to_sdk(m.name()),
751                        datatype_name: identifier_core_to_sdk(m.identifier_at(struct_handle.name)),
752                        package,
753                    }
754                })
755                .chain(m.enum_defs().iter().map(|enum_def| {
756                    let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
757                    let package = ObjectId::new(m.self_id().address().into_bytes());
758                    TypeOrigin {
759                        module_name: identifier_core_to_sdk(m.name()),
760                        datatype_name: identifier_core_to_sdk(m.identifier_at(enum_handle.name)),
761                        package,
762                    }
763                }))
764        })
765        .collect()
766}
767
768fn build_upgraded_type_origin_table(
769    predecessor: &MovePackage,
770    modules: &[CompiledModule],
771    storage_id: ObjectId,
772) -> Result<Vec<TypeOrigin>, ExecutionError> {
773    let mut new_table = vec![];
774    let mut existing_table = predecessor.type_origin_map();
775    for m in modules {
776        for struct_def in m.struct_defs() {
777            let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
778            let module_name = identifier_core_to_sdk(m.name());
779            let struct_name = identifier_core_to_sdk(m.identifier_at(struct_handle.name));
780            let mod_key = (module_name.clone(), struct_name.clone());
781            // if id exists in the predecessor's table, use it, otherwise use the id of the
782            // upgraded module
783            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
784            new_table.push(TypeOrigin {
785                module_name,
786                datatype_name: struct_name,
787                package,
788            });
789        }
790
791        for enum_def in m.enum_defs() {
792            let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
793            let module_name = identifier_core_to_sdk(m.name());
794            let enum_name = identifier_core_to_sdk(m.identifier_at(enum_handle.name));
795            let mod_key = (module_name.clone(), enum_name.clone());
796            // if id exists in the predecessor's table, use it, otherwise use the id of the
797            // upgraded module
798            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
799            new_table.push(TypeOrigin {
800                module_name,
801                datatype_name: enum_name,
802                package,
803            });
804        }
805    }
806
807    if !existing_table.is_empty() {
808        Err(ExecutionError::from_kind(
809            ExecutionErrorKind::PackageUpgradeError {
810                kind: PackageUpgradeError::IncompatibleUpgrade,
811            },
812        ))
813    } else {
814        Ok(new_table)
815    }
816}
817
818/// Protocol-dependent switches that the low-level package build and
819/// verification routines need.
820///
821/// Derived from the network's [`ProtocolConfig`], it lets those routines depend
822/// on a small, explicit set of protocol-gated flags rather than the full
823/// [`ProtocolConfig`].
824#[derive(Debug, Clone, Copy, Default)]
825pub struct ProtocolBuildConfig {
826    /// Build the module metadata with view function information and enable the
827    /// verifier to check the correctness of the view function attribute.
828    pub allow_view_function: bool,
829}
830
831impl ProtocolBuildConfig {
832    /// Derives the build config from a network [`ProtocolConfig`].
833    pub fn from_protocol_config(protocol_config: &ProtocolConfig) -> Self {
834        Self {
835            allow_view_function: protocol_config.package_metadata_with_dynamic_module_metadata(),
836        }
837    }
838}
839
840impl From<&ProtocolConfig> for ProtocolBuildConfig {
841    fn from(protocol_config: &ProtocolConfig) -> Self {
842        Self::from_protocol_config(protocol_config)
843    }
844}
845
846impl From<Option<&ProtocolConfig>> for ProtocolBuildConfig {
847    fn from(protocol_config: Option<&ProtocolConfig>) -> Self {
848        protocol_config
849            .map(Self::from_protocol_config)
850            .unwrap_or_default()
851    }
852}
853
854/// IOTA specific metadata attached to the metadata section of file_format.
855#[serde_as]
856#[derive(Debug, Clone, Serialize, Deserialize)]
857pub struct RuntimeModuleMetadataWrapper {
858    pub version: u64,
859    #[serde_as(as = "Bytes")]
860    pub inner: Vec<u8>,
861}
862
863impl RuntimeModuleMetadataWrapper {
864    pub fn to_bcs_bytes(&self) -> Vec<u8> {
865        // Safe unwrap as the RuntimeModuleMetadataWrapper struct is always serializable
866        bcs::to_bytes(&self).unwrap()
867    }
868
869    pub fn try_into_runtime_module_metadata(
870        &self,
871        protocol_build_config: &ProtocolBuildConfig,
872    ) -> Result<RuntimeModuleMetadata, IotaError> {
873        match self.version {
874            1 => {
875                let inner: RuntimeModuleMetadataV1 = bcs::from_bytes(&self.inner).map_err(|e| {
876                    IotaError::RuntimeModuleMetadataDeserialization {
877                        error: e.to_string(),
878                    }
879                })?;
880                Ok(RuntimeModuleMetadata::V1(inner))
881            }
882            2 if protocol_build_config.allow_view_function => {
883                let inner: RuntimeModuleMetadataV2 = bcs::from_bytes(&self.inner).map_err(|e| {
884                    IotaError::RuntimeModuleMetadataDeserialization {
885                        error: e.to_string(),
886                    }
887                })?;
888                Ok(RuntimeModuleMetadata::V2(inner))
889            }
890            _ => Err(IotaError::RuntimeModuleMetadataDeserialization {
891                error: format!(
892                    "Unsupported runtime module metadata version: {}",
893                    self.version
894                ),
895            }),
896        }
897    }
898}
899
900impl From<RuntimeModuleMetadata> for RuntimeModuleMetadataWrapper {
901    fn from(metadata: RuntimeModuleMetadata) -> Self {
902        match metadata {
903            RuntimeModuleMetadata::V1(inner) => RuntimeModuleMetadataWrapper {
904                version: 1,
905                inner: inner.to_bcs_bytes(),
906            },
907            RuntimeModuleMetadata::V2(inner) => RuntimeModuleMetadataWrapper {
908                version: 2,
909                inner: inner.to_bcs_bytes(),
910            },
911        }
912    }
913}
914
915/// IOTA specific metadata attached to the metadata section of file_format.
916#[derive(Debug, Clone, Serialize, Deserialize)]
917pub enum RuntimeModuleMetadata {
918    V1(RuntimeModuleMetadataV1),
919    V2(RuntimeModuleMetadataV2),
920}
921
922impl RuntimeModuleMetadata {
923    pub fn v1() -> Self {
924        RuntimeModuleMetadata::V1(RuntimeModuleMetadataV1::default())
925    }
926
927    pub fn v2() -> Self {
928        RuntimeModuleMetadata::V2(RuntimeModuleMetadataV2::default())
929    }
930
931    /// Records `attribute` for `function_name`.
932    ///
933    /// The attribute's version must match the metadata's version: a
934    /// [`IotaAttribute::V1`] belongs in [`RuntimeModuleMetadata::V1`] and a
935    /// [`IotaAttribute::V2`] in [`RuntimeModuleMetadata::V2`].
936    ///
937    /// # Panics
938    ///
939    /// Panics if the attribute's version does not match the metadata's
940    /// version.
941    pub fn add_function_attribute(&mut self, function_name: String, attribute: IotaAttribute) {
942        match (self, attribute) {
943            (RuntimeModuleMetadata::V1(metadata), IotaAttribute::V1(attribute)) => {
944                metadata.add_function_attribute(function_name, attribute)
945            }
946            (RuntimeModuleMetadata::V2(metadata), IotaAttribute::V2(attribute)) => {
947                metadata.add_function_attribute(function_name, attribute)
948            }
949            _ => panic!("attribute version does not match runtime module metadata version"),
950        }
951    }
952
953    pub fn is_empty(&self) -> bool {
954        match self {
955            RuntimeModuleMetadata::V1(metadata) => metadata.is_empty(),
956            RuntimeModuleMetadata::V2(metadata) => metadata.is_empty(),
957        }
958    }
959}
960
961/// Version-agnostic wrapper over the IOTA attribute types, for passing an
962/// attribute of either version to [`RuntimeModuleMetadata`].
963///
964/// This wrapper is an in-memory convenience only and is never serialized.
965#[derive(Debug, Clone)]
966pub enum IotaAttribute {
967    V1(IotaAttributeV1),
968    V2(IotaAttributeV2),
969}
970
971/// The list of iota attribute types recognized by the compiler.
972#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
973pub enum IotaAttributeV1 {
974    Authenticator(AuthenticatorAttribute),
975}
976
977/// The list of iota attribute types recognized by the compiler.
978#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
979pub enum IotaAttributeV2 {
980    Authenticator(AuthenticatorAttribute),
981    View,
982}
983
984#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
985pub struct AuthenticatorAttribute {
986    pub version: u8,
987}
988
989impl IotaAttributeV1 {
990    pub fn authenticator_attribute(version: u8) -> Self {
991        IotaAttributeV1::Authenticator(AuthenticatorAttribute { version })
992    }
993}
994
995impl IotaAttributeV2 {
996    pub fn authenticator_attribute(version: u8) -> Self {
997        IotaAttributeV2::Authenticator(AuthenticatorAttribute { version })
998    }
999
1000    pub fn view_attribute() -> Self {
1001        IotaAttributeV2::View
1002    }
1003}
1004
1005/// V1 of IOTA specific metadata.
1006#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1007pub struct RuntimeModuleMetadataV1 {
1008    /// Attributes attached to functions, by definition index.
1009    pub fun_attributes: BTreeMap<String, Vec<IotaAttributeV1>>,
1010}
1011
1012/// V2 of IOTA specific metadata.
1013#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1014pub struct RuntimeModuleMetadataV2 {
1015    /// Attributes attached to functions, by definition index.
1016    pub fun_attributes: BTreeMap<String, Vec<IotaAttributeV2>>,
1017}
1018
1019impl RuntimeModuleMetadataV1 {
1020    pub fn add_function_attribute(&mut self, function_name: String, attribute: IotaAttributeV1) {
1021        self.fun_attributes
1022            .entry(function_name)
1023            .or_default()
1024            .push(attribute);
1025    }
1026
1027    pub fn is_empty(&self) -> bool {
1028        self.fun_attributes.is_empty()
1029    }
1030
1031    pub fn fun_attributes_iter(&self) -> impl Iterator<Item = (&String, &Vec<IotaAttributeV1>)> {
1032        self.fun_attributes.iter()
1033    }
1034
1035    pub fn to_bcs_bytes(&self) -> Vec<u8> {
1036        // Safe unwrap as the RuntimeModuleMetadataV1 struct is always serializable
1037        bcs::to_bytes(&self).unwrap()
1038    }
1039}
1040
1041impl RuntimeModuleMetadataV2 {
1042    pub fn add_function_attribute(&mut self, function_name: String, attribute: IotaAttributeV2) {
1043        self.fun_attributes
1044            .entry(function_name)
1045            .or_default()
1046            .push(attribute);
1047    }
1048
1049    pub fn is_empty(&self) -> bool {
1050        self.fun_attributes.is_empty()
1051    }
1052    pub fn fun_attributes_iter(&self) -> impl Iterator<Item = (&String, &Vec<IotaAttributeV2>)> {
1053        self.fun_attributes.iter()
1054    }
1055
1056    pub fn to_bcs_bytes(&self) -> Vec<u8> {
1057        // Safe unwrap as the RuntimeModuleMetadataV2 struct is always serializable
1058        bcs::to_bytes(&self).unwrap()
1059    }
1060}
1061
1062/// Enum for handling the PackageMetadata framework type. The PackageMetadata is
1063/// IOTA specific metadata derived from a package and readable on-chain. This
1064/// enums helps with the versioning, which is actually used as the object
1065/// content, i.e., PackageMetadataV1 is the type used on-chain.
1066#[derive(Debug, Clone, Serialize, Deserialize)]
1067pub enum PackageMetadata {
1068    V1(PackageMetadataV1),
1069}
1070
1071impl PackageMetadata {
1072    /// Create a `PackageMetadata` for the newly
1073    /// published/upgraded package at `package_id`
1074    pub fn new_v1(
1075        uid: ObjectId,
1076        storage_id: ObjectId,
1077        runtime_id: ObjectId,
1078        package_version: u64,
1079        modules_metadata_map: BTreeMap<String, BTreeMap<String, TypeTag>>,
1080    ) -> Self {
1081        PackageMetadata::V1(PackageMetadataV1::new(
1082            uid,
1083            storage_id,
1084            runtime_id,
1085            package_version,
1086            modules_metadata_map,
1087        ))
1088    }
1089
1090    pub fn type_(&self) -> StructTag {
1091        match self {
1092            PackageMetadata::V1(_) => PackageMetadataV1::type_(),
1093        }
1094    }
1095
1096    pub fn to_bcs_bytes(&self) -> Vec<u8> {
1097        match self {
1098            PackageMetadata::V1(inner) => inner.to_bcs_bytes(),
1099        }
1100    }
1101}
1102
1103#[derive(Debug, Default, Serialize, Deserialize, Clone, Eq, PartialEq)]
1104pub struct PackageMetadataKey {
1105    // This field is required to make a Rust struct compatible with an empty Move one.
1106    // An empty Move struct contains a 1-byte dummy bool field because empty fields are not
1107    // allowed in the bytecode.
1108    dummy_field: bool,
1109}
1110
1111impl PackageMetadataKey {
1112    pub fn tag() -> StructTag {
1113        StructTag::new(
1114            Address::FRAMEWORK,
1115            PACKAGE_METADATA_MODULE_NAME,
1116            PACKAGE_METADATA_KEY_STRUCT_NAME,
1117            Vec::new(),
1118        )
1119    }
1120
1121    pub fn to_bcs_bytes(&self) -> Vec<u8> {
1122        // Safe unwrap as the PackageMetadataKey struct is always serializable
1123        bcs::to_bytes(&self).unwrap()
1124    }
1125}
1126
1127pub fn derive_package_metadata_id(package_storage_id: ObjectId) -> ObjectId {
1128    derived_object::derive_object_id(
1129        package_storage_id,
1130        &PackageMetadataKey::tag().into(),
1131        &PackageMetadataKey::default().to_bcs_bytes(),
1132    )
1133    .unwrap() // safe because type tag is known
1134}
1135
1136/// V1 of IOTA specific package metadata.
1137#[derive(Debug, Clone, Serialize, Deserialize)]
1138pub struct PackageMetadataV1 {
1139    // The package metadata object UID
1140    pub uid: UID,
1141    /// Storage ID of the package represented by this metadata
1142    /// The object id of the runtime package metadata object is derived from
1143    /// this value.
1144    pub storage_id: ID,
1145    /// Runtime ID of the package represented by this metadata. Runtime ID is
1146    /// the Storage ID of the first version of a package.
1147    pub runtime_id: ID,
1148    /// Version of the package represented by this metadata
1149    pub package_version: u64,
1150    // Handles to internal package modules
1151    pub modules_metadata: VecMap<String, ModuleMetadataV1>,
1152}
1153
1154impl PackageMetadataV1 {
1155    fn new(
1156        uid: ObjectId,
1157        storage_id: ObjectId,
1158        runtime_id: ObjectId,
1159        package_version: u64,
1160        modules_metadata_map: BTreeMap<String, BTreeMap<String, TypeTag>>,
1161    ) -> Self {
1162        let mut modules_metadata = VecMap { contents: vec![] };
1163
1164        for (module_name, module_metadata_map) in modules_metadata_map {
1165            let mut module_metadata = ModuleMetadataV1 {
1166                authenticator_metadata: vec![],
1167            };
1168            for (function_name, account_type) in module_metadata_map {
1169                module_metadata
1170                    .authenticator_metadata
1171                    .push(AuthenticatorMetadataV1 {
1172                        function_name,
1173                        account_type,
1174                    });
1175            }
1176            modules_metadata.contents.push(Entry {
1177                key: module_name,
1178                value: module_metadata,
1179            });
1180        }
1181
1182        Self {
1183            uid: UID::new(uid),
1184            storage_id: ID::new(storage_id),
1185            runtime_id: ID::new(runtime_id),
1186            package_version,
1187            modules_metadata,
1188        }
1189    }
1190
1191    pub fn type_() -> StructTag {
1192        StructTag::new(
1193            Address::FRAMEWORK,
1194            PACKAGE_METADATA_MODULE_NAME,
1195            PACKAGE_METADATA_V1_STRUCT_NAME,
1196            vec![],
1197        )
1198    }
1199
1200    pub fn to_bcs_bytes(&self) -> Vec<u8> {
1201        // Safe unwrap as the PackageMetadataV1 struct is always serializable
1202        bcs::to_bytes(&self).unwrap()
1203    }
1204}
1205
1206/// V1 of IOTA specific module metadata. Only includes authenticator info.
1207#[derive(Debug, Clone, Serialize, Deserialize)]
1208pub struct ModuleMetadataV1 {
1209    pub authenticator_metadata: Vec<AuthenticatorMetadataV1>,
1210}
1211
1212impl ModuleMetadataV1 {
1213    pub fn is_empty(&self) -> bool {
1214        self.authenticator_metadata.is_empty()
1215    }
1216}
1217
1218/// V1 of IOTA specific authenticator info metadata.
1219#[serde_as]
1220#[derive(Debug, Clone, Serialize, Deserialize)]
1221pub struct AuthenticatorMetadataV1 {
1222    pub function_name: String,
1223    #[serde_as(as = "TypeName")]
1224    pub account_type: TypeTag,
1225}