1use crate::execution_status::PackageUpgradeError;
5use crate::{
6    SUI_FRAMEWORK_ADDRESS,
7    base_types::{ObjectID, SequenceNumber},
8    crypto::DefaultHash,
9    error::{ExecutionError, ExecutionErrorKind, SuiErrorKind, SuiResult},
10    id::{ID, UID},
11    object::OBJECT_START_VERSION,
12};
13use fastcrypto::hash::HashFunction;
14use move_binary_format::binary_config::BinaryConfig;
15use move_binary_format::file_format::CompiledModule;
16use move_binary_format::file_format_common::VERSION_6;
17use move_binary_format::normalized;
18use move_core_types::language_storage::ModuleId;
19use move_core_types::{
20    account_address::AccountAddress,
21    ident_str,
22    identifier::{IdentStr, Identifier},
23    language_storage::StructTag,
24};
25use once_cell::sync::Lazy;
26use schemars::JsonSchema;
27use serde::{Deserialize, Serialize};
28use serde_with::Bytes;
29use serde_with::serde_as;
30use std::collections::{BTreeMap, BTreeSet};
31use std::hash::Hash;
32use sui_protocol_config::ProtocolConfig;
33
34pub const PACKAGE_MODULE_NAME: &IdentStr = ident_str!("package");
40pub const UPGRADECAP_STRUCT_NAME: &IdentStr = ident_str!("UpgradeCap");
41pub const UPGRADETICKET_STRUCT_NAME: &IdentStr = ident_str!("UpgradeTicket");
42pub const UPGRADERECEIPT_STRUCT_NAME: &IdentStr = ident_str!("UpgradeReceipt");
43
44const DEFAULT_MAX_DISASSEMBLED_MODULE_SIZE: Option<usize> = Some(1024 * 1024);
46
47const MAX_DISASSEMBLED_MODULE_SIZE_ENV: &str = "MAX_DISASSEMBLED_MODULE_SIZE";
48pub static MAX_DISASSEMBLED_MODULE_SIZE: Lazy<Option<usize>> = Lazy::new(|| {
49    let max_bound_opt: Option<usize> = std::env::var(MAX_DISASSEMBLED_MODULE_SIZE_ENV)
50        .ok()
51        .and_then(|s| s.parse().ok());
52    match max_bound_opt {
53        Some(0) => None,
54        Some(max_bound) => Some(max_bound),
55        None => DEFAULT_MAX_DISASSEMBLED_MODULE_SIZE,
56    }
57});
58
59#[derive(Clone, Debug)]
60pub struct FnInfo {
62    pub is_test: bool,
64}
65
66#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
67pub struct FnInfoKey {
69    pub fn_name: String,
70    pub mod_addr: AccountAddress,
71}
72
73pub type FnInfoMap = BTreeMap<FnInfoKey, FnInfo>;
75
76#[derive(
78    Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, Hash, JsonSchema,
79)]
80pub struct TypeOrigin {
81    pub module_name: String,
82    #[serde(alias = "struct_name")]
84    pub datatype_name: String,
85    pub package: ObjectID,
86}
87
88#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash, JsonSchema)]
90pub struct UpgradeInfo {
91    pub upgraded_id: ObjectID,
93    pub upgraded_version: SequenceNumber,
95}
96
97#[serde_as]
99#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
100pub struct MovePackage {
101    id: ObjectID,
102    version: SequenceNumber,
113    #[serde_as(as = "BTreeMap<_, Bytes>")]
115    module_map: BTreeMap<String, Vec<u8>>,
116
117    type_origin_table: Vec<TypeOrigin>,
120
121    linkage_table: BTreeMap<ObjectID, UpgradeInfo>,
124}
125
126#[repr(u8)]
130#[derive(derive_more::Display, Debug, Clone, Copy)]
131pub enum UpgradePolicy {
132    #[display("COMPATIBLE")]
133    Compatible = 0,
134    #[display("ADDITIVE")]
135    Additive = 128,
136    #[display("DEP_ONLY")]
137    DepOnly = 192,
138}
139
140impl UpgradePolicy {
141    pub const COMPATIBLE: u8 = Self::Compatible as u8;
143    pub const ADDITIVE: u8 = Self::Additive as u8;
144    pub const DEP_ONLY: u8 = Self::DepOnly as u8;
145
146    pub fn is_valid_policy(policy: &u8) -> bool {
147        Self::try_from(*policy).is_ok()
148    }
149}
150
151impl TryFrom<u8> for UpgradePolicy {
152    type Error = ();
153    fn try_from(value: u8) -> Result<Self, Self::Error> {
154        match value {
155            x if x == Self::Compatible as u8 => Ok(Self::Compatible),
156            x if x == Self::Additive as u8 => Ok(Self::Additive),
157            x if x == Self::DepOnly as u8 => Ok(Self::DepOnly),
158            _ => Err(()),
159        }
160    }
161}
162
163#[derive(Debug, Serialize, Deserialize)]
165pub struct UpgradeCap {
166    pub id: UID,
167    pub package: ID,
168    pub version: u64,
169    pub policy: u8,
170}
171
172#[derive(Debug, Serialize, Deserialize)]
174pub struct UpgradeTicket {
175    pub cap: ID,
176    pub package: ID,
177    pub policy: u8,
178    pub digest: Vec<u8>,
179}
180
181#[derive(Debug, Serialize, Deserialize)]
183pub struct UpgradeReceipt {
184    pub cap: ID,
185    pub package: ID,
186}
187
188impl MovePackage {
189    pub fn new(
192        id: ObjectID,
193        version: SequenceNumber,
194        module_map: BTreeMap<String, Vec<u8>>,
195        max_move_package_size: u64,
196        type_origin_table: Vec<TypeOrigin>,
197        linkage_table: BTreeMap<ObjectID, UpgradeInfo>,
198    ) -> Result<Self, ExecutionError> {
199        let pkg = Self {
200            id,
201            version,
202            module_map,
203            type_origin_table,
204            linkage_table,
205        };
206        let object_size = pkg.size() as u64;
207        if object_size > max_move_package_size {
208            return Err(ExecutionErrorKind::MovePackageTooBig {
209                object_size,
210                max_object_size: max_move_package_size,
211            }
212            .into());
213        }
214        Ok(pkg)
215    }
216
217    pub fn digest(&self, hash_modules: bool) -> [u8; 32] {
218        Self::compute_digest_for_modules_and_deps(
219            self.module_map.values(),
220            self.linkage_table
221                .values()
222                .map(|UpgradeInfo { upgraded_id, .. }| upgraded_id),
223            hash_modules,
224        )
225    }
226
227    pub fn compute_digest_for_modules_and_deps<'a>(
230        modules: impl IntoIterator<Item = &'a Vec<u8>>,
231        object_ids: impl IntoIterator<Item = &'a ObjectID>,
232        hash_modules: bool,
233    ) -> [u8; 32] {
234        let mut module_digests: Vec<[u8; 32]>;
235        let mut components: Vec<&[u8]> = vec![];
236        if !hash_modules {
237            for module in modules {
238                components.push(module.as_ref())
239            }
240        } else {
241            module_digests = vec![];
242            for module in modules {
243                let mut digest = DefaultHash::default();
244                digest.update(module);
245                module_digests.push(digest.finalize().digest);
246            }
247            components.extend(module_digests.iter().map(|d| d.as_ref()))
248        }
249
250        components.extend(object_ids.into_iter().map(|o| o.as_ref()));
251        components.sort();
253
254        let mut digest = DefaultHash::default();
255        for c in components {
256            digest.update(c);
257        }
258        digest.finalize().digest
259    }
260
261    pub fn new_initial<'p>(
264        modules: &[CompiledModule],
265        protocol_config: &ProtocolConfig,
266        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
267    ) -> Result<Self, ExecutionError> {
268        let module = modules
269            .first()
270            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
271        let runtime_id = ObjectID::from(*module.address());
272        let storage_id = runtime_id;
273        let type_origin_table = build_initial_type_origin_table(modules);
274        Self::from_module_iter_with_type_origin_table(
275            storage_id,
276            runtime_id,
277            OBJECT_START_VERSION,
278            modules,
279            protocol_config,
280            type_origin_table,
281            transitive_dependencies,
282        )
283    }
284
285    pub fn new_upgraded<'p>(
288        &self,
289        storage_id: ObjectID,
290        modules: &[CompiledModule],
291        protocol_config: &ProtocolConfig,
292        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
293    ) -> Result<Self, ExecutionError> {
294        let module = modules
295            .first()
296            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
297        let runtime_id = ObjectID::from(*module.address());
298        let type_origin_table =
299            build_upgraded_type_origin_table(self, modules, storage_id, protocol_config)?;
300        let mut new_version = self.version();
301        new_version.increment();
302        Self::from_module_iter_with_type_origin_table(
303            storage_id,
304            runtime_id,
305            new_version,
306            modules,
307            protocol_config,
308            type_origin_table,
309            transitive_dependencies,
310        )
311    }
312
313    pub fn new_system(
314        version: SequenceNumber,
315        modules: &[CompiledModule],
316        dependencies: impl IntoIterator<Item = ObjectID>,
317    ) -> Self {
318        let module = modules
319            .first()
320            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
321
322        let storage_id = ObjectID::from(*module.address());
323        let type_origin_table = build_initial_type_origin_table(modules);
324
325        let linkage_table = BTreeMap::from_iter(dependencies.into_iter().map(|dep| {
326            let info = UpgradeInfo {
327                upgraded_id: dep,
328                upgraded_version: SequenceNumber::new(),
340            };
341            (dep, info)
342        }));
343
344        let module_map = BTreeMap::from_iter(modules.iter().map(|module| {
345            let name = module.name().to_string();
346            let mut bytes = Vec::new();
347            module
348                .serialize_with_version(module.version, &mut bytes)
349                .unwrap();
350            (name, bytes)
351        }));
352
353        Self::new(
354            storage_id,
355            version,
356            module_map,
357            u64::MAX, type_origin_table,
359            linkage_table,
360        )
361        .expect("System packages are not subject to a size limit")
362    }
363
364    fn from_module_iter_with_type_origin_table<'p>(
365        storage_id: ObjectID,
366        self_id: ObjectID,
367        version: SequenceNumber,
368        modules: &[CompiledModule],
369        protocol_config: &ProtocolConfig,
370        type_origin_table: Vec<TypeOrigin>,
371        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
372    ) -> Result<Self, ExecutionError> {
373        let mut module_map = BTreeMap::new();
374        let mut immediate_dependencies = BTreeSet::new();
375
376        for module in modules {
377            let name = module.name().to_string();
378
379            immediate_dependencies.extend(
380                module
381                    .immediate_dependencies()
382                    .into_iter()
383                    .map(|dep| ObjectID::from(*dep.address())),
384            );
385
386            let mut bytes = Vec::new();
387            let version = if protocol_config.move_binary_format_version() > VERSION_6 {
388                module.version
389            } else {
390                VERSION_6
391            };
392            module.serialize_with_version(version, &mut bytes).unwrap();
393            module_map.insert(name, bytes);
394        }
395
396        immediate_dependencies.remove(&self_id);
397        let linkage_table = build_linkage_table(
398            immediate_dependencies,
399            transitive_dependencies,
400            protocol_config,
401        )?;
402        Self::new(
403            storage_id,
404            version,
405            module_map,
406            protocol_config.max_move_package_size(),
407            type_origin_table,
408            linkage_table,
409        )
410    }
411
412    pub fn get_module(&self, storage_id: &ModuleId) -> Option<&Vec<u8>> {
418        if self.id != ObjectID::from(*storage_id.address()) {
419            None
420        } else {
421            self.module_map.get(&storage_id.name().to_string())
422        }
423    }
424
425    pub fn size(&self) -> usize {
427        let module_map_size = self
428            .module_map
429            .iter()
430            .map(|(name, module)| name.len() + module.len())
431            .sum::<usize>();
432        let type_origin_table_size = self
433            .type_origin_table
434            .iter()
435            .map(
436                |TypeOrigin {
437                     module_name,
438                     datatype_name: struct_name,
439                     ..
440                 }| module_name.len() + struct_name.len() + ObjectID::LENGTH,
441            )
442            .sum::<usize>();
443
444        let linkage_table_size = self.linkage_table.len()
445            * (ObjectID::LENGTH + (ObjectID::LENGTH + 8));
446
447        8 + module_map_size + type_origin_table_size + linkage_table_size
448    }
449
450    pub fn id(&self) -> ObjectID {
451        self.id
452    }
453
454    pub fn version(&self) -> SequenceNumber {
455        self.version
456    }
457
458    pub fn decrement_version(&mut self) {
459        self.version.decrement();
460    }
461
462    pub fn increment_version(&mut self) {
463        self.version.increment();
464    }
465
466    pub fn object_size_for_gas_metering(&self) -> usize {
468        self.size()
469    }
470
471    pub fn serialized_module_map(&self) -> &BTreeMap<String, Vec<u8>> {
472        &self.module_map
473    }
474
475    pub fn type_origin_table(&self) -> &Vec<TypeOrigin> {
476        &self.type_origin_table
477    }
478
479    pub fn type_origin_map(&self) -> BTreeMap<(String, String), ObjectID> {
480        self.type_origin_table
481            .iter()
482            .map(
483                |TypeOrigin {
484                     module_name,
485                     datatype_name: struct_name,
486                     package,
487                 }| { ((module_name.clone(), struct_name.clone()), *package) },
488            )
489            .collect()
490    }
491
492    pub fn linkage_table(&self) -> &BTreeMap<ObjectID, UpgradeInfo> {
493        &self.linkage_table
494    }
495
496    pub fn original_package_id(&self) -> ObjectID {
499        if self.version == OBJECT_START_VERSION {
500            return self.id;
502        }
503        let bytes = self.module_map.values().next().expect("Empty module map");
504        let module = CompiledModule::deserialize_with_defaults(bytes)
505            .expect("A Move package contains a module that cannot be deserialized");
506        (*module.address()).into()
507    }
508
509    pub fn deserialize_module(
510        &self,
511        module: &Identifier,
512        binary_config: &BinaryConfig,
513    ) -> SuiResult<CompiledModule> {
514        self.deserialize_module_by_str(module.as_str(), binary_config)
515    }
516
517    pub fn deserialize_module_by_str(
518        &self,
519        module: &str,
520        binary_config: &BinaryConfig,
521    ) -> SuiResult<CompiledModule> {
522        let bytes = self.serialized_module_map().get(module).ok_or_else(|| {
524            SuiErrorKind::ModuleNotFound {
525                module_name: module.to_string(),
526            }
527        })?;
528        CompiledModule::deserialize_with_config(bytes, binary_config).map_err(|error| {
529            SuiErrorKind::ModuleDeserializationFailure {
530                error: error.to_string(),
531            }
532            .into()
533        })
534    }
535
536    pub fn normalize<S: Hash + Eq + Clone + ToString, Pool: normalized::StringPool<String = S>>(
539        &self,
540        pool: &mut Pool,
541        binary_config: &BinaryConfig,
542        include_code: bool,
543    ) -> SuiResult<BTreeMap<String, normalized::Module<S>>> {
544        normalize_modules(pool, self.module_map.values(), binary_config, include_code)
545    }
546}
547
548impl UpgradeCap {
549    pub fn type_() -> StructTag {
550        StructTag {
551            address: SUI_FRAMEWORK_ADDRESS,
552            module: PACKAGE_MODULE_NAME.to_owned(),
553            name: UPGRADECAP_STRUCT_NAME.to_owned(),
554            type_params: vec![],
555        }
556    }
557
558    pub fn new(uid: ObjectID, package_id: ObjectID) -> Self {
561        UpgradeCap {
562            id: UID::new(uid),
563            package: ID::new(package_id),
564            version: 1,
565            policy: UpgradePolicy::COMPATIBLE,
566        }
567    }
568}
569
570impl UpgradeTicket {
571    pub fn type_() -> StructTag {
572        StructTag {
573            address: SUI_FRAMEWORK_ADDRESS,
574            module: PACKAGE_MODULE_NAME.to_owned(),
575            name: UPGRADETICKET_STRUCT_NAME.to_owned(),
576            type_params: vec![],
577        }
578    }
579}
580
581impl UpgradeReceipt {
582    pub fn type_() -> StructTag {
583        StructTag {
584            address: SUI_FRAMEWORK_ADDRESS,
585            module: PACKAGE_MODULE_NAME.to_owned(),
586            name: UPGRADERECEIPT_STRUCT_NAME.to_owned(),
587            type_params: vec![],
588        }
589    }
590
591    pub fn new(upgrade_ticket: UpgradeTicket, upgraded_package_id: ObjectID) -> Self {
594        UpgradeReceipt {
595            cap: upgrade_ticket.cap,
596            package: ID::new(upgraded_package_id),
597        }
598    }
599}
600
601pub fn is_test_fun(name: &IdentStr, module: &CompiledModule, fn_info_map: &FnInfoMap) -> bool {
603    let fn_name = name.to_string();
604    let mod_handle = module.self_handle();
605    let mod_addr = *module.address_identifier_at(mod_handle.address);
606    let fn_info_key = FnInfoKey { fn_name, mod_addr };
607    match fn_info_map.get(&fn_info_key) {
608        Some(fn_info) => fn_info.is_test,
609        None => false,
610    }
611}
612
613pub fn normalize_modules<
616    'a,
617    S: Hash + Eq + Clone + ToString,
618    Pool: normalized::StringPool<String = S>,
619    I,
620>(
621    pool: &mut Pool,
622    modules: I,
623    binary_config: &BinaryConfig,
624    include_code: bool,
625) -> SuiResult<BTreeMap<String, normalized::Module<S>>>
626where
627    I: Iterator<Item = &'a Vec<u8>>,
628{
629    let mut normalized_modules = BTreeMap::new();
630    for bytecode in modules {
631        let module =
632            CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
633                SuiErrorKind::ModuleDeserializationFailure {
634                    error: error.to_string(),
635                }
636            })?;
637        let normalized_module = normalized::Module::new(pool, &module, include_code);
638        normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
639    }
640    Ok(normalized_modules)
641}
642
643pub fn normalize_deserialized_modules<
646    'a,
647    S: Hash + Eq + Clone + ToString,
648    Pool: normalized::StringPool<String = S>,
649    I,
650>(
651    pool: &mut Pool,
652    modules: I,
653    include_code: bool,
654) -> BTreeMap<String, normalized::Module<S>>
655where
656    I: Iterator<Item = &'a CompiledModule>,
657{
658    let mut normalized_modules = BTreeMap::new();
659    for module in modules {
660        let normalized_module = normalized::Module::new(pool, module, include_code);
661        normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
662    }
663    normalized_modules
664}
665
666fn build_linkage_table<'p>(
667    mut immediate_dependencies: BTreeSet<ObjectID>,
668    transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
669    protocol_config: &ProtocolConfig,
670) -> Result<BTreeMap<ObjectID, UpgradeInfo>, ExecutionError> {
671    let mut linkage_table = BTreeMap::new();
672    let mut dep_linkage_tables = vec![];
673
674    for transitive_dep in transitive_dependencies.into_iter() {
675        let original_id = transitive_dep.original_package_id();
679
680        let imm_dep = immediate_dependencies.remove(&original_id);
681
682        if protocol_config.dependency_linkage_error() {
683            dep_linkage_tables.push(&transitive_dep.linkage_table);
684            let existing = linkage_table.insert(
685                original_id,
686                UpgradeInfo {
687                    upgraded_id: transitive_dep.id,
688                    upgraded_version: transitive_dep.version,
689                },
690            );
691
692            if existing.is_some() {
693                return Err(ExecutionErrorKind::InvalidLinkage.into());
694            }
695        } else {
696            if imm_dep {
697                dep_linkage_tables.push(&transitive_dep.linkage_table);
700            }
701            linkage_table.insert(
702                original_id,
703                UpgradeInfo {
704                    upgraded_id: transitive_dep.id,
705                    upgraded_version: transitive_dep.version,
706                },
707            );
708        }
709    }
710    if !immediate_dependencies.is_empty() {
712        return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
713    }
714
715    for dep_linkage_table in dep_linkage_tables {
717        for (original_id, dep_info) in dep_linkage_table {
718            let Some(our_info) = linkage_table.get(original_id) else {
719                return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
720            };
721
722            if our_info.upgraded_version < dep_info.upgraded_version {
723                return Err(ExecutionErrorKind::PublishUpgradeDependencyDowngrade.into());
724            }
725        }
726    }
727
728    Ok(linkage_table)
729}
730
731fn build_initial_type_origin_table(modules: &[CompiledModule]) -> Vec<TypeOrigin> {
732    modules
733        .iter()
734        .flat_map(|m| {
735            m.struct_defs()
736                .iter()
737                .map(|struct_def| {
738                    let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
739                    let module_name = m.name().to_string();
740                    let struct_name = m.identifier_at(struct_handle.name).to_string();
741                    let package: ObjectID = (*m.self_id().address()).into();
742                    TypeOrigin {
743                        module_name,
744                        datatype_name: struct_name,
745                        package,
746                    }
747                })
748                .chain(m.enum_defs().iter().map(|enum_def| {
749                    let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
750                    let module_name = m.name().to_string();
751                    let enum_name = m.identifier_at(enum_handle.name).to_string();
752                    let package: ObjectID = (*m.self_id().address()).into();
753                    TypeOrigin {
754                        module_name,
755                        datatype_name: enum_name,
756                        package,
757                    }
758                }))
759        })
760        .collect()
761}
762
763fn build_upgraded_type_origin_table(
764    predecessor: &MovePackage,
765    modules: &[CompiledModule],
766    storage_id: ObjectID,
767    protocol_config: &ProtocolConfig,
768) -> Result<Vec<TypeOrigin>, ExecutionError> {
769    let mut new_table = vec![];
770    let mut existing_table = predecessor.type_origin_map();
771    for m in modules {
772        for struct_def in m.struct_defs() {
773            let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
774            let module_name = m.name().to_string();
775            let struct_name = m.identifier_at(struct_handle.name).to_string();
776            let mod_key = (module_name.clone(), struct_name.clone());
777            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
780            new_table.push(TypeOrigin {
781                module_name,
782                datatype_name: struct_name,
783                package,
784            });
785        }
786
787        for enum_def in m.enum_defs() {
788            let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
789            let module_name = m.name().to_string();
790            let enum_name = m.identifier_at(enum_handle.name).to_string();
791            let mod_key = (module_name.clone(), enum_name.clone());
792            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
795            new_table.push(TypeOrigin {
796                module_name,
797                datatype_name: enum_name,
798                package,
799            });
800        }
801    }
802
803    if !existing_table.is_empty() {
804        if protocol_config.missing_type_is_compatibility_error() {
805            Err(ExecutionError::from_kind(
806                ExecutionErrorKind::PackageUpgradeError {
807                    upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
808                },
809            ))
810        } else {
811            Err(ExecutionError::invariant_violation(
812                "Package upgrade missing type from previous version.",
813            ))
814        }
815    } else {
816        Ok(new_table)
817    }
818}