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}