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