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