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 package may be
19//!   found in storage.
20//! - `Runtime ID` will always mean the `Package ID`/`Storage ID` of the initially published
21//!   package. For a non upgradeable package this will always be equal to `Storage ID`. For an
22//!   upgradeable package, it will be the `Storage ID` of the package's first deployed version.
23//! - `Storage ID` is the `Package ID`, and it is mostly used in to highlight that we are talking
24//!   about the current `Package ID` and not the `Runtime ID`
25//! - `Module ID` is the the type [ModuleID](move_core_types::language_storage::ModuleId).
26//!
27//! Some of these are redundant and have overlapping meaning, so whenever
28//! reasonable/necessary the possible naming will be listed. From all of these
29//! `Runtime ID` and `Module ID` are the most confusing. `Module ID` may be used
30//! with `Runtime ID` and `Storage ID` depending on the context. While `Runtime
31//! ID` is mostly used in name resolution during runtime, when a package with
32//! its modules has been loaded.
33
34use std::{
35    collections::{BTreeMap, BTreeSet},
36    hash::Hash,
37};
38
39use derive_more::Display;
40use iota_protocol_config::ProtocolConfig;
41use iota_sdk_move_types::iota_framework::vec_map::{Entry, VecMap};
42use iota_sdk_types::{
43    Identifier, MovePackage, ObjectId, PackageUpgradeError, StructTag, TypeOrigin, TypeTag,
44    UpgradeInfo, Version,
45};
46use move_binary_format::{
47    binary_config::BinaryConfig,
48    file_format::CompiledModule,
49    file_format_common::{IOTA_METADATA_KEY, VERSION_6},
50    normalized,
51};
52use move_core_types::identifier::IdentStr;
53use serde::{Deserialize, Serialize};
54use serde_with::{Bytes, serde_as};
55
56use crate::{
57    Address,
58    error::{ExecutionError, ExecutionErrorKind, IotaError, IotaResult},
59    id::{ID, UID},
60    iota_sdk_types_conversions::identifier_core_to_sdk,
61    iota_serde::TypeName,
62};
63
64pub const PACKAGE_METADATA_MODULE_NAME: Identifier = Identifier::from_static("package_metadata");
65pub const PACKAGE_METADATA_V1_STRUCT_NAME: Identifier =
66    Identifier::from_static("PackageMetadataV1");
67pub const PACKAGE_METADATA_KEY_STRUCT_NAME: Identifier =
68    Identifier::from_static("PackageMetadataKey");
69
70#[derive(Clone, Debug)]
71/// Additional information about a function
72pub struct FnInfo {
73    /// If true, it's a function involved in testing (`[test]`, `[test_only]`,
74    /// `[expected_failure]`)
75    pub is_test: bool,
76    /// If set, function was marked to represent authenticator function of
77    /// given version.
78    pub authenticator_version: Option<u8>,
79    pub is_view: bool,
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: Address,
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: Version,
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: Version,
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: Version,
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: Version::default(),
299            };
300            (dep, info)
301        }));
302
303        let module_map = BTreeMap::from_iter(modules.iter().map(|module| {
304            let name = identifier_core_to_sdk(module.name());
305            let mut bytes = Vec::new();
306            module
307                .serialize_with_version(module.version, &mut bytes)
308                .unwrap();
309            (name, bytes)
310        }));
311
312        // Not size-checked: an upgrade of a system package is exempt from the
313        // bound, and `compare_system_package` checks a package being added
314        // itself, against `max_package_size`.
315        MovePackage::new(
316            storage_id,
317            version,
318            module_map,
319            type_origin_table,
320            linkage_table,
321        )
322    }
323
324    fn from_module_iter_with_type_origin_table<'p>(
325        storage_id: ObjectId,
326        self_id: ObjectId,
327        version: Version,
328        modules: &[CompiledModule],
329        protocol_config: &ProtocolConfig,
330        type_origin_table: Vec<TypeOrigin>,
331        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
332    ) -> Result<MovePackage, ExecutionError> {
333        let mut module_map = BTreeMap::new();
334        let mut immediate_dependencies = BTreeSet::new();
335
336        for module in modules {
337            let name = identifier_core_to_sdk(module.name());
338
339            immediate_dependencies.extend(
340                module
341                    .immediate_dependencies()
342                    .into_iter()
343                    .map(|dep| ObjectId::new(dep.address().into_bytes())),
344            );
345
346            let mut bytes = Vec::new();
347            let version = if protocol_config.move_binary_format_version() > VERSION_6 {
348                module.version
349            } else {
350                VERSION_6
351            };
352            module.serialize_with_version(version, &mut bytes).unwrap();
353            module_map.insert(name, bytes);
354        }
355
356        immediate_dependencies.remove(&self_id);
357        let linkage_table = build_linkage_table(
358            immediate_dependencies,
359            transitive_dependencies,
360            protocol_config,
361        )?;
362
363        let package = MovePackage::new(
364            storage_id,
365            version,
366            module_map,
367            type_origin_table,
368            linkage_table,
369        );
370        // This is the one path that writes a package other than a system
371        // package upgrade: a first publish -- by a user, at genesis, or of a
372        // system package added at an epoch change -- and a user package
373        // upgrade. So the bound for the package's address is applied here. A
374        // system package upgrade goes through `new_system` and is exempt, and
375        // a package read back from the network is rebuilt with
376        // `MovePackage::new` and not checked again.
377        package.check_size(max_package_size(storage_id, protocol_config))?;
378
379        Ok(package)
380    }
381
382    /// The `Package ID` of the first version of this package.
383    ///
384    /// Also referred to as `Runtime ID`.
385    ///
386    /// Regardless of which version of the package we are working with, this
387    /// function will always return the `Package ID`/`Storage ID` of the first
388    /// package version in the version chain.
389    fn original_package_id(&self) -> ObjectId {
390        if self.version == Version::OBJECT_START {
391            // for a non-upgraded package, original ID is just the package ID
392            return self.id;
393        }
394
395        let bytes = self.modules.values().next().expect("Empty module map");
396        // Remember, that all modules will contain the `Package ID` of the first
397        // deployed package. This is why taking any of them will produce the
398        // original package id.
399        let module = CompiledModule::deserialize_with_defaults(bytes)
400            .expect("A Move package contains a module that cannot be deserialized");
401        ObjectId::new(module.address().into_bytes())
402    }
403
404    fn deserialize_module(
405        &self,
406        module: &Identifier,
407        binary_config: &BinaryConfig,
408    ) -> IotaResult<CompiledModule> {
409        // TODO use the session's cache
410        let bytes =
411            self.serialized_module_map()
412                .get(module)
413                .ok_or_else(|| IotaError::ModuleNotFound {
414                    module_name: module.to_string(),
415                })?;
416
417        CompiledModule::deserialize_with_config(bytes, binary_config).map_err(|error| {
418            IotaError::ModuleDeserializationFailure {
419                error: error.to_string(),
420            }
421        })
422    }
423
424    /// If `include_code` is set to `false`, the normalized module will skip
425    /// function bodies but still include the signatures.
426    fn normalize<S: Hash + Eq + Clone + ToString, Pool: normalized::StringPool<String = S>>(
427        &self,
428        pool: &mut Pool,
429        binary_config: &BinaryConfig,
430        include_code: bool,
431    ) -> IotaResult<BTreeMap<String, normalized::Module<S>>> {
432        normalize_modules(pool, self.modules.values(), binary_config, include_code)
433    }
434}
435
436impl UpgradeCap {
437    /// Create an `UpgradeCap` for the newly published package at `package_id`,
438    /// and associate it with the fresh `uid`.
439    pub fn new(uid: ObjectId, package_id: ObjectId) -> Self {
440        UpgradeCap {
441            id: UID::new(uid),
442            package: ID::new(package_id),
443            version: 1,
444            policy: UpgradePolicy::COMPATIBLE,
445        }
446    }
447}
448
449impl UpgradeReceipt {
450    /// Create an `UpgradeReceipt` for the upgraded package at `package_id`
451    /// using the `UpgradeTicket` and newly published package id.
452    pub fn new(upgrade_ticket: UpgradeTicket, upgraded_package_id: ObjectId) -> Self {
453        UpgradeReceipt {
454            cap: upgrade_ticket.cap,
455            package: ID::new(upgraded_package_id),
456        }
457    }
458}
459
460/// Checks if a function is annotated with one of the test-related annotations
461pub fn is_test_fun(name: &str, module: &CompiledModule, fn_info_map: &FnInfoMap) -> bool {
462    let mod_handle = module.self_handle();
463    let mod_addr = Address::new(
464        module
465            .address_identifier_at(mod_handle.address)
466            .into_bytes(),
467    );
468    let mod_name = module.name().to_string();
469    let fn_info_key = FnInfoKey {
470        fn_name: name.to_string(),
471        mod_name,
472        mod_addr,
473    };
474    match fn_info_map.get(&fn_info_key) {
475        Some(fn_info) => fn_info.is_test,
476        None => false,
477    }
478}
479
480pub fn get_authenticator_version_from_fun(
481    name: &str,
482    module: &CompiledModule,
483    fn_info_map: &FnInfoMap,
484) -> Option<u8> {
485    let mod_handle = module.self_handle();
486    let mod_addr = Address::from(
487        module
488            .address_identifier_at(mod_handle.address)
489            .into_bytes(),
490    );
491    let mod_name = module.name().to_string();
492    let fn_info_key = FnInfoKey {
493        fn_name: name.to_string(),
494        mod_name,
495        mod_addr,
496    };
497    match fn_info_map.get(&fn_info_key) {
498        Some(FnInfo {
499            is_test: _,
500            authenticator_version: Some(v),
501            is_view: _,
502        }) => Some(*v),
503        _ => None,
504    }
505}
506
507/// Returns true if a function is marked as a view function.
508pub fn is_view_function_from_fn_info(
509    name: &IdentStr,
510    module: &CompiledModule,
511    fn_info_map: &FnInfoMap,
512) -> bool {
513    let fn_name = name.to_string();
514    let mod_handle = module.self_handle();
515    let mod_addr = Address::from(
516        module
517            .address_identifier_at(mod_handle.address)
518            .into_bytes(),
519    );
520    let mod_name = module.name().to_string();
521    let fn_info_key = FnInfoKey {
522        fn_name,
523        mod_name,
524        mod_addr,
525    };
526    fn_info_map
527        .get(&fn_info_key)
528        .map(|info| info.is_view)
529        .unwrap_or(false)
530}
531
532/// If `include_code` is set to `false`, the normalized module will skip
533/// function bodies but still include the signatures.
534pub fn normalize_modules<
535    'a,
536    S: Hash + Eq + Clone + ToString,
537    Pool: normalized::StringPool<String = S>,
538    I,
539>(
540    pool: &mut Pool,
541    modules: I,
542    binary_config: &BinaryConfig,
543    include_code: bool,
544) -> IotaResult<BTreeMap<String, normalized::Module<S>>>
545where
546    I: Iterator<Item = &'a Vec<u8>>,
547{
548    let mut normalized_modules = BTreeMap::new();
549    for bytecode in modules {
550        let module =
551            CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
552                IotaError::ModuleDeserializationFailure {
553                    error: error.to_string(),
554                }
555            })?;
556        let normalized_module = normalized::Module::new(pool, &module, include_code);
557        normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
558    }
559    Ok(normalized_modules)
560}
561
562/// If `include_code` is set to `false`, the normalized module will skip
563/// function bodies but still include the signatures.
564///
565/// The returned metadata is the IOTA-specific runtime metadata attached to the
566/// module, or the default empty metadata when the module has no IOTA metadata.
567pub fn normalize_modules_with_metadata<
568    'a,
569    S: Hash + Eq + Clone + ToString,
570    Pool: normalized::StringPool<String = S>,
571    I,
572>(
573    pool: &mut Pool,
574    modules: I,
575    binary_config: &BinaryConfig,
576    include_code: bool,
577    protocol_config: Option<&ProtocolConfig>,
578) -> IotaResult<BTreeMap<String, (normalized::Module<S>, RuntimeModuleMetadata)>>
579where
580    I: Iterator<Item = &'a Vec<u8>>,
581{
582    let mut normalized_modules = BTreeMap::new();
583    for bytecode in modules {
584        let module =
585            CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
586                IotaError::ModuleDeserializationFailure {
587                    error: error.to_string(),
588                }
589            })?;
590        let metadata = runtime_module_metadata(&module, protocol_config)?;
591        let normalized_module = normalized::Module::new(pool, &module, include_code);
592        normalized_modules.insert(
593            normalized_module.name().to_string(),
594            (normalized_module, metadata),
595        );
596    }
597    Ok(normalized_modules)
598}
599
600/// If `include_code` is set to `false`, the normalized module will skip
601/// function bodies but still include the signatures.
602pub fn normalize_deserialized_modules<
603    'a,
604    S: Hash + Eq + Clone + ToString,
605    Pool: normalized::StringPool<String = S>,
606    I,
607>(
608    pool: &mut Pool,
609    modules: I,
610    include_code: bool,
611) -> BTreeMap<String, normalized::Module<S>>
612where
613    I: Iterator<Item = &'a CompiledModule>,
614{
615    let mut normalized_modules = BTreeMap::new();
616    for module in modules {
617        let normalized_module = normalized::Module::new(pool, module, include_code);
618        normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
619    }
620    normalized_modules
621}
622
623/// If `include_code` is set to `false`, the normalized module will skip
624/// function bodies but still include the signatures.
625///
626/// The returned metadata is the IOTA-specific runtime metadata attached to the
627/// module, or the default empty metadata when the module has no IOTA metadata.
628pub fn normalize_deserialized_modules_with_metadata<
629    'a,
630    S: Hash + Eq + Clone + ToString,
631    Pool: normalized::StringPool<String = S>,
632    I,
633>(
634    pool: &mut Pool,
635    modules: I,
636    include_code: bool,
637    protocol_config: Option<&ProtocolConfig>,
638) -> IotaResult<BTreeMap<String, (normalized::Module<S>, RuntimeModuleMetadata)>>
639where
640    I: Iterator<Item = &'a CompiledModule>,
641{
642    let mut normalized_modules = BTreeMap::new();
643    for module in modules {
644        let metadata = runtime_module_metadata(module, protocol_config)?;
645        let normalized_module = normalized::Module::new(pool, module, include_code);
646        normalized_modules.insert(
647            normalized_module.name().to_string(),
648            (normalized_module, metadata),
649        );
650    }
651    Ok(normalized_modules)
652}
653
654fn runtime_module_metadata(
655    module: &CompiledModule,
656    protocol_config: Option<&ProtocolConfig>,
657) -> IotaResult<RuntimeModuleMetadata> {
658    let build_config = ProtocolBuildConfig::from(protocol_config);
659    let Some(metadata) = module
660        .metadata
661        .iter()
662        .find(|metadata| metadata.key == IOTA_METADATA_KEY)
663    else {
664        if build_config.allow_view_function {
665            return Ok(RuntimeModuleMetadata::v2());
666        } else {
667            return Ok(RuntimeModuleMetadata::v1());
668        }
669    };
670
671    let metadata_wrapper: RuntimeModuleMetadataWrapper =
672        bcs::from_bytes(&metadata.value).map_err(|error| {
673            IotaError::RuntimeModuleMetadataDeserialization {
674                error: error.to_string(),
675            }
676        })?;
677    metadata_wrapper.try_into_runtime_module_metadata(&build_config)
678}
679
680/// The size bound a package published at `storage_id` must stay within.
681///
682/// A system package published for the first time — at genesis, or when a new
683/// one is added at an epoch change — goes through the same path as a user
684/// package, but is not a user package and is bound by
685/// `max_move_system_package_size`. When that is unset, system packages are
686/// bound by `max_move_package_size` like any other package. Upgrades of an
687/// existing system package never reach this check at all (see
688/// `MovePackage::new_system`).
689pub fn max_package_size(storage_id: ObjectId, protocol_config: &ProtocolConfig) -> u64 {
690    if storage_id.is_system_package() {
691        protocol_config
692            .max_move_system_package_size_as_option()
693            .unwrap_or_else(|| protocol_config.max_move_package_size())
694    } else {
695        protocol_config.max_move_package_size()
696    }
697}
698
699fn build_linkage_table<'p>(
700    mut immediate_dependencies: BTreeSet<ObjectId>,
701    transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
702    protocol_config: &ProtocolConfig,
703) -> Result<BTreeMap<ObjectId, UpgradeInfo>, ExecutionError> {
704    let mut linkage_table = BTreeMap::new();
705    let mut dep_linkage_tables = vec![];
706
707    for transitive_dep in transitive_dependencies.into_iter() {
708        // original_package_id will deserialize a module but only for the purpose of
709        // obtaining "original ID" of the package containing it so using max
710        // Move binary version during deserialization is OK
711        let original_id = MovePackage::original_package_id(transitive_dep);
712
713        let imm_dep = immediate_dependencies.remove(&original_id);
714
715        if protocol_config.dependency_linkage_error() {
716            dep_linkage_tables.push(&transitive_dep.linkage_table);
717
718            let existing = linkage_table.insert(
719                original_id,
720                UpgradeInfo {
721                    upgraded_id: transitive_dep.id,
722                    upgraded_version: transitive_dep.version,
723                },
724            );
725
726            if existing.is_some() {
727                return Err(ExecutionErrorKind::InvalidLinkage.into());
728            }
729        } else {
730            if imm_dep {
731                // Found an immediate dependency, mark it as seen, and stash a reference to its
732                // linkage table to check later.
733                dep_linkage_tables.push(&transitive_dep.linkage_table);
734            }
735            linkage_table.insert(
736                original_id,
737                UpgradeInfo {
738                    upgraded_id: transitive_dep.id,
739                    upgraded_version: transitive_dep.version,
740                },
741            );
742        }
743    }
744    // (1) Every dependency is represented in the transitive dependencies
745    if !immediate_dependencies.is_empty() {
746        return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
747    }
748
749    // (2) Every dependency's linkage table is superseded by this linkage table
750    for dep_linkage_table in dep_linkage_tables {
751        for (original_id, dep_info) in dep_linkage_table {
752            let Some(our_info) = linkage_table.get(original_id) else {
753                return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
754            };
755
756            if our_info.upgraded_version < dep_info.upgraded_version {
757                return Err(ExecutionErrorKind::PublishUpgradeDependencyDowngrade.into());
758            }
759        }
760    }
761
762    Ok(linkage_table)
763}
764
765fn build_initial_type_origin_table(modules: &[CompiledModule]) -> Vec<TypeOrigin> {
766    modules
767        .iter()
768        .flat_map(|m| {
769            m.struct_defs()
770                .iter()
771                .map(|struct_def| {
772                    let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
773                    let package = ObjectId::new(m.self_id().address().into_bytes());
774                    TypeOrigin {
775                        module_name: identifier_core_to_sdk(m.name()),
776                        datatype_name: identifier_core_to_sdk(m.identifier_at(struct_handle.name)),
777                        package,
778                    }
779                })
780                .chain(m.enum_defs().iter().map(|enum_def| {
781                    let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
782                    let package = ObjectId::new(m.self_id().address().into_bytes());
783                    TypeOrigin {
784                        module_name: identifier_core_to_sdk(m.name()),
785                        datatype_name: identifier_core_to_sdk(m.identifier_at(enum_handle.name)),
786                        package,
787                    }
788                }))
789        })
790        .collect()
791}
792
793fn build_upgraded_type_origin_table(
794    predecessor: &MovePackage,
795    modules: &[CompiledModule],
796    storage_id: ObjectId,
797) -> Result<Vec<TypeOrigin>, ExecutionError> {
798    let mut new_table = vec![];
799    let mut existing_table = predecessor.type_origin_map();
800    for m in modules {
801        for struct_def in m.struct_defs() {
802            let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
803            let module_name = identifier_core_to_sdk(m.name());
804            let struct_name = identifier_core_to_sdk(m.identifier_at(struct_handle.name));
805            let mod_key = (module_name.clone(), struct_name.clone());
806            // if id exists in the predecessor's table, use it, otherwise use the id of the
807            // upgraded module
808            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
809            new_table.push(TypeOrigin {
810                module_name,
811                datatype_name: struct_name,
812                package,
813            });
814        }
815
816        for enum_def in m.enum_defs() {
817            let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
818            let module_name = identifier_core_to_sdk(m.name());
819            let enum_name = identifier_core_to_sdk(m.identifier_at(enum_handle.name));
820            let mod_key = (module_name.clone(), enum_name.clone());
821            // if id exists in the predecessor's table, use it, otherwise use the id of the
822            // upgraded module
823            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
824            new_table.push(TypeOrigin {
825                module_name,
826                datatype_name: enum_name,
827                package,
828            });
829        }
830    }
831
832    if !existing_table.is_empty() {
833        Err(ExecutionError::from_kind(
834            ExecutionErrorKind::PackageUpgradeError {
835                kind: PackageUpgradeError::IncompatibleUpgrade,
836            },
837        ))
838    } else {
839        Ok(new_table)
840    }
841}
842
843/// Protocol-dependent switches that the low-level package build and
844/// verification routines need.
845///
846/// Derived from the network's [`ProtocolConfig`], it lets those routines depend
847/// on a small, explicit set of protocol-gated flags rather than the full
848/// [`ProtocolConfig`].
849///
850/// The [`Default`] value is meant for builds whose target network is unknown:
851/// it allows view functions, which every public network has enabled.
852#[derive(Debug, Clone, Copy)]
853pub struct ProtocolBuildConfig {
854    /// Build the module metadata with view function information and enable the
855    /// verifier to check the correctness of the view function attribute.
856    pub allow_view_function: bool,
857    /// Maximum size (in bytes) a published package may occupy on-chain. `None`
858    /// when the config was not derived from a network protocol config, in which
859    /// case the real limit is unknown.
860    pub max_move_package_size: Option<u64>,
861    /// Maximum size (in bytes) a published system package may occupy on-chain.
862    /// Set together with `max_move_package_size`: `None` next to a known user
863    /// bound means the network's protocol version holds system packages to
864    /// `max_move_package_size`, while `None` next to an unknown user bound
865    /// means the real limit is unknown too.
866    pub max_move_system_package_size: Option<u64>,
867}
868
869impl Default for ProtocolBuildConfig {
870    fn default() -> Self {
871        Self {
872            allow_view_function: true,
873            max_move_package_size: None,
874            max_move_system_package_size: None,
875        }
876    }
877}
878
879impl ProtocolBuildConfig {
880    /// Derives the build config from a network [`ProtocolConfig`].
881    pub fn from_protocol_config(protocol_config: &ProtocolConfig) -> Self {
882        Self {
883            allow_view_function: protocol_config.package_metadata_with_dynamic_module_metadata(),
884            max_move_package_size: Some(protocol_config.max_move_package_size()),
885            max_move_system_package_size: protocol_config.max_move_system_package_size_as_option(),
886        }
887    }
888}
889
890impl From<&ProtocolConfig> for ProtocolBuildConfig {
891    fn from(protocol_config: &ProtocolConfig) -> Self {
892        Self::from_protocol_config(protocol_config)
893    }
894}
895
896impl From<Option<&ProtocolConfig>> for ProtocolBuildConfig {
897    fn from(protocol_config: Option<&ProtocolConfig>) -> Self {
898        protocol_config
899            .map(Self::from_protocol_config)
900            .unwrap_or_default()
901    }
902}
903
904/// IOTA specific metadata attached to the metadata section of file_format.
905#[serde_as]
906#[derive(Debug, Clone, Serialize, Deserialize)]
907pub struct RuntimeModuleMetadataWrapper {
908    pub version: u64,
909    #[serde_as(as = "Bytes")]
910    pub inner: Vec<u8>,
911}
912
913impl RuntimeModuleMetadataWrapper {
914    pub fn to_bcs_bytes(&self) -> Vec<u8> {
915        // Safe unwrap as the RuntimeModuleMetadataWrapper struct is always serializable
916        bcs::to_bytes(&self).unwrap()
917    }
918
919    pub fn try_into_runtime_module_metadata(
920        &self,
921        protocol_build_config: &ProtocolBuildConfig,
922    ) -> Result<RuntimeModuleMetadata, IotaError> {
923        match self.version {
924            1 => {
925                let inner: RuntimeModuleMetadataV1 = bcs::from_bytes(&self.inner).map_err(|e| {
926                    IotaError::RuntimeModuleMetadataDeserialization {
927                        error: e.to_string(),
928                    }
929                })?;
930                Ok(RuntimeModuleMetadata::V1(inner))
931            }
932            2 if protocol_build_config.allow_view_function => {
933                let inner: RuntimeModuleMetadataV2 = bcs::from_bytes(&self.inner).map_err(|e| {
934                    IotaError::RuntimeModuleMetadataDeserialization {
935                        error: e.to_string(),
936                    }
937                })?;
938                Ok(RuntimeModuleMetadata::V2(inner))
939            }
940            _ => Err(IotaError::RuntimeModuleMetadataDeserialization {
941                error: format!(
942                    "Unsupported runtime module metadata version: {}",
943                    self.version
944                ),
945            }),
946        }
947    }
948}
949
950impl From<RuntimeModuleMetadata> for RuntimeModuleMetadataWrapper {
951    fn from(metadata: RuntimeModuleMetadata) -> Self {
952        match metadata {
953            RuntimeModuleMetadata::V1(inner) => RuntimeModuleMetadataWrapper {
954                version: 1,
955                inner: inner.to_bcs_bytes(),
956            },
957            RuntimeModuleMetadata::V2(inner) => RuntimeModuleMetadataWrapper {
958                version: 2,
959                inner: inner.to_bcs_bytes(),
960            },
961        }
962    }
963}
964
965/// IOTA specific metadata attached to the metadata section of file_format.
966#[derive(Debug, Clone, Serialize, Deserialize)]
967pub enum RuntimeModuleMetadata {
968    V1(RuntimeModuleMetadataV1),
969    V2(RuntimeModuleMetadataV2),
970}
971
972impl RuntimeModuleMetadata {
973    pub fn v1() -> Self {
974        RuntimeModuleMetadata::V1(RuntimeModuleMetadataV1::default())
975    }
976
977    pub fn v2() -> Self {
978        RuntimeModuleMetadata::V2(RuntimeModuleMetadataV2::default())
979    }
980
981    /// Records `attribute` for `function_name`.
982    ///
983    /// The attribute's version must match the metadata's version: a
984    /// [`IotaAttribute::V1`] belongs in [`RuntimeModuleMetadata::V1`] and a
985    /// [`IotaAttribute::V2`] in [`RuntimeModuleMetadata::V2`].
986    ///
987    /// # Panics
988    ///
989    /// Panics if the attribute's version does not match the metadata's
990    /// version.
991    pub fn add_function_attribute(&mut self, function_name: String, attribute: IotaAttribute) {
992        match (self, attribute) {
993            (RuntimeModuleMetadata::V1(metadata), IotaAttribute::V1(attribute)) => {
994                metadata.add_function_attribute(function_name, attribute)
995            }
996            (RuntimeModuleMetadata::V2(metadata), IotaAttribute::V2(attribute)) => {
997                metadata.add_function_attribute(function_name, attribute)
998            }
999            _ => panic!("attribute version does not match runtime module metadata version"),
1000        }
1001    }
1002
1003    pub fn is_empty(&self) -> bool {
1004        match self {
1005            RuntimeModuleMetadata::V1(metadata) => metadata.is_empty(),
1006            RuntimeModuleMetadata::V2(metadata) => metadata.is_empty(),
1007        }
1008    }
1009}
1010
1011/// Version-agnostic wrapper over the IOTA attribute types, for passing an
1012/// attribute of either version to [`RuntimeModuleMetadata`].
1013///
1014/// This wrapper is an in-memory convenience only and is never serialized.
1015#[derive(Debug, Clone)]
1016pub enum IotaAttribute {
1017    V1(IotaAttributeV1),
1018    V2(IotaAttributeV2),
1019}
1020
1021/// The list of iota attribute types recognized by the compiler.
1022#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1023pub enum IotaAttributeV1 {
1024    Authenticator(AuthenticatorAttribute),
1025}
1026
1027/// The list of iota attribute types recognized by the compiler.
1028#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1029pub enum IotaAttributeV2 {
1030    Authenticator(AuthenticatorAttribute),
1031    View,
1032}
1033
1034#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1035pub struct AuthenticatorAttribute {
1036    pub version: u8,
1037}
1038
1039impl IotaAttributeV1 {
1040    pub fn authenticator_attribute(version: u8) -> Self {
1041        IotaAttributeV1::Authenticator(AuthenticatorAttribute { version })
1042    }
1043}
1044
1045impl IotaAttributeV2 {
1046    pub fn authenticator_attribute(version: u8) -> Self {
1047        IotaAttributeV2::Authenticator(AuthenticatorAttribute { version })
1048    }
1049
1050    pub fn view_attribute() -> Self {
1051        IotaAttributeV2::View
1052    }
1053}
1054
1055/// V1 of IOTA specific metadata.
1056#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1057pub struct RuntimeModuleMetadataV1 {
1058    /// Attributes attached to functions, by definition index.
1059    pub fun_attributes: BTreeMap<String, Vec<IotaAttributeV1>>,
1060}
1061
1062/// V2 of IOTA specific metadata.
1063#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1064pub struct RuntimeModuleMetadataV2 {
1065    /// Attributes attached to functions, by definition index.
1066    pub fun_attributes: BTreeMap<String, Vec<IotaAttributeV2>>,
1067}
1068
1069impl RuntimeModuleMetadataV1 {
1070    pub fn add_function_attribute(&mut self, function_name: String, attribute: IotaAttributeV1) {
1071        self.fun_attributes
1072            .entry(function_name)
1073            .or_default()
1074            .push(attribute);
1075    }
1076
1077    pub fn is_empty(&self) -> bool {
1078        self.fun_attributes.is_empty()
1079    }
1080
1081    pub fn fun_attributes_iter(&self) -> impl Iterator<Item = (&String, &Vec<IotaAttributeV1>)> {
1082        self.fun_attributes.iter()
1083    }
1084
1085    pub fn to_bcs_bytes(&self) -> Vec<u8> {
1086        // Safe unwrap as the RuntimeModuleMetadataV1 struct is always serializable
1087        bcs::to_bytes(&self).unwrap()
1088    }
1089}
1090
1091impl RuntimeModuleMetadataV2 {
1092    pub fn add_function_attribute(&mut self, function_name: String, attribute: IotaAttributeV2) {
1093        self.fun_attributes
1094            .entry(function_name)
1095            .or_default()
1096            .push(attribute);
1097    }
1098
1099    pub fn is_empty(&self) -> bool {
1100        self.fun_attributes.is_empty()
1101    }
1102    pub fn fun_attributes_iter(&self) -> impl Iterator<Item = (&String, &Vec<IotaAttributeV2>)> {
1103        self.fun_attributes.iter()
1104    }
1105
1106    pub fn to_bcs_bytes(&self) -> Vec<u8> {
1107        // Safe unwrap as the RuntimeModuleMetadataV2 struct is always serializable
1108        bcs::to_bytes(&self).unwrap()
1109    }
1110}
1111
1112/// Enum for handling the PackageMetadata framework type. The PackageMetadata is
1113/// IOTA specific metadata derived from a package and readable on-chain. This
1114/// enums helps with the versioning, which is actually used as the object
1115/// content, i.e., PackageMetadataV1 is the type used on-chain.
1116#[derive(Debug, Clone, Serialize, Deserialize)]
1117pub enum PackageMetadata {
1118    V1(PackageMetadataV1),
1119}
1120
1121impl PackageMetadata {
1122    /// Create a `PackageMetadata` for the newly
1123    /// published/upgraded package at `package_id`
1124    pub fn new_v1(
1125        uid: ObjectId,
1126        storage_id: ObjectId,
1127        runtime_id: ObjectId,
1128        package_version: u64,
1129        modules_metadata_map: BTreeMap<String, BTreeMap<String, TypeTag>>,
1130    ) -> Self {
1131        PackageMetadata::V1(PackageMetadataV1::new(
1132            uid,
1133            storage_id,
1134            runtime_id,
1135            package_version,
1136            modules_metadata_map,
1137        ))
1138    }
1139
1140    pub fn struct_tag(&self) -> StructTag {
1141        match self {
1142            PackageMetadata::V1(_) => StructTag::new_package_metadata_v1(),
1143        }
1144    }
1145
1146    pub fn to_bcs_bytes(&self) -> Vec<u8> {
1147        match self {
1148            PackageMetadata::V1(inner) => inner.to_bcs_bytes(),
1149        }
1150    }
1151}
1152
1153#[derive(Debug, Default, Serialize, Deserialize, Clone, Eq, PartialEq)]
1154pub struct PackageMetadataKey {
1155    // This field is required to make a Rust struct compatible with an empty Move one.
1156    // An empty Move struct contains a 1-byte dummy bool field because empty fields are not
1157    // allowed in the bytecode.
1158    dummy_field: bool,
1159}
1160
1161impl PackageMetadataKey {
1162    pub fn to_bcs_bytes(&self) -> Vec<u8> {
1163        // Safe unwrap as the PackageMetadataKey struct is always serializable
1164        bcs::to_bytes(&self).unwrap()
1165    }
1166}
1167
1168pub fn derive_package_metadata_id(package_storage_id: ObjectId) -> ObjectId {
1169    package_storage_id.derive_object_id(
1170        &StructTag::new_package_metadata_key().into(),
1171        &PackageMetadataKey::default().to_bcs_bytes(),
1172    )
1173}
1174
1175/// V1 of IOTA specific package metadata.
1176#[derive(Debug, Clone, Serialize, Deserialize)]
1177pub struct PackageMetadataV1 {
1178    // The package metadata object UID
1179    pub uid: UID,
1180    /// Storage ID of the package represented by this metadata
1181    /// The object id of the runtime package metadata object is derived from
1182    /// this value.
1183    pub storage_id: ID,
1184    /// Runtime ID of the package represented by this metadata. Runtime ID is
1185    /// the Storage ID of the first version of a package.
1186    pub runtime_id: ID,
1187    /// Version of the package represented by this metadata
1188    pub package_version: u64,
1189    // Handles to internal package modules
1190    pub modules_metadata: VecMap<String, ModuleMetadataV1>,
1191}
1192
1193impl PackageMetadataV1 {
1194    fn new(
1195        uid: ObjectId,
1196        storage_id: ObjectId,
1197        runtime_id: ObjectId,
1198        package_version: u64,
1199        modules_metadata_map: BTreeMap<String, BTreeMap<String, TypeTag>>,
1200    ) -> Self {
1201        let mut modules_metadata = VecMap { contents: vec![] };
1202
1203        for (module_name, module_metadata_map) in modules_metadata_map {
1204            let mut module_metadata = ModuleMetadataV1 {
1205                authenticator_metadata: vec![],
1206            };
1207            for (function_name, account_type) in module_metadata_map {
1208                module_metadata
1209                    .authenticator_metadata
1210                    .push(AuthenticatorMetadataV1 {
1211                        function_name,
1212                        account_type,
1213                    });
1214            }
1215            modules_metadata.contents.push(Entry {
1216                key: module_name,
1217                value: module_metadata,
1218            });
1219        }
1220
1221        Self {
1222            uid: UID::new(uid),
1223            storage_id: ID::new(storage_id),
1224            runtime_id: ID::new(runtime_id),
1225            package_version,
1226            modules_metadata,
1227        }
1228    }
1229
1230    pub fn to_bcs_bytes(&self) -> Vec<u8> {
1231        // Safe unwrap as the PackageMetadataV1 struct is always serializable
1232        bcs::to_bytes(&self).unwrap()
1233    }
1234}
1235
1236/// V1 of IOTA specific module metadata. Only includes authenticator info.
1237#[derive(Debug, Clone, Serialize, Deserialize)]
1238pub struct ModuleMetadataV1 {
1239    pub authenticator_metadata: Vec<AuthenticatorMetadataV1>,
1240}
1241
1242impl ModuleMetadataV1 {
1243    pub fn is_empty(&self) -> bool {
1244        self.authenticator_metadata.is_empty()
1245    }
1246}
1247
1248/// V1 of IOTA specific authenticator info metadata.
1249#[serde_as]
1250#[derive(Debug, Clone, Serialize, Deserialize)]
1251pub struct AuthenticatorMetadataV1 {
1252    pub function_name: String,
1253    #[serde_as(as = "TypeName")]
1254    pub account_type: TypeTag,
1255}
1256
1257#[cfg(test)]
1258mod tests {
1259    use iota_protocol_config::{Chain, ProtocolVersion};
1260
1261    use super::*;
1262
1263    /// A protocol version where `max_move_system_package_size` is unset.
1264    const VERSION_WITHOUT_SYSTEM_PACKAGE_SIZE: u64 = 37;
1265
1266    fn config(version: u64) -> ProtocolConfig {
1267        ProtocolConfig::get_for_version(ProtocolVersion::new(version), Chain::Unknown)
1268    }
1269
1270    #[test]
1271    fn user_packages_keep_the_user_bound() {
1272        let protocol_config = config(ProtocolVersion::MAX.as_u64());
1273        let user_package = ObjectId::random();
1274
1275        assert!(!user_package.is_system_package());
1276        assert_eq!(
1277            max_package_size(user_package, &protocol_config),
1278            protocol_config.max_move_package_size(),
1279        );
1280    }
1281
1282    #[test]
1283    fn system_packages_get_the_system_bound() {
1284        let protocol_config = config(ProtocolVersion::MAX.as_u64());
1285
1286        assert_eq!(
1287            max_package_size(ObjectId::FRAMEWORK, &protocol_config),
1288            protocol_config.max_move_system_package_size(),
1289        );
1290        assert!(
1291            protocol_config.max_move_system_package_size()
1292                > protocol_config.max_move_package_size(),
1293        );
1294    }
1295
1296    #[test]
1297    fn system_packages_fall_back_to_the_user_bound_when_unset() {
1298        let protocol_config = config(VERSION_WITHOUT_SYSTEM_PACKAGE_SIZE);
1299
1300        assert!(
1301            protocol_config
1302                .max_move_system_package_size_as_option()
1303                .is_none()
1304        );
1305        assert_eq!(
1306            max_package_size(ObjectId::FRAMEWORK, &protocol_config),
1307            protocol_config.max_move_package_size(),
1308        );
1309    }
1310}