1use std::collections::{BTreeMap, BTreeSet};
6
7use derive_more::Display;
8use fastcrypto::hash::HashFunction;
9use iota_protocol_config::ProtocolConfig;
10use move_binary_format::{
11 binary_config::BinaryConfig, file_format::CompiledModule, file_format_common::VERSION_6,
12 normalized,
13};
14use move_core_types::{
15 account_address::AccountAddress,
16 ident_str,
17 identifier::{IdentStr, Identifier},
18 language_storage::{ModuleId, StructTag},
19};
20use move_disassembler::disassembler::Disassembler;
21use move_ir_types::location::Spanned;
22use schemars::JsonSchema;
23use serde::{Deserialize, Serialize};
24use serde_json::Value;
25use serde_with::{Bytes, serde_as};
26
27use crate::{
28 IOTA_FRAMEWORK_ADDRESS,
29 base_types::{ObjectID, SequenceNumber},
30 crypto::DefaultHash,
31 error::{ExecutionError, ExecutionErrorKind, IotaError, IotaResult},
32 execution_status::PackageUpgradeError,
33 id::{ID, UID},
34 object::OBJECT_START_VERSION,
35};
36
37pub const PACKAGE_MODULE_NAME: &IdentStr = ident_str!("package");
43pub const UPGRADECAP_STRUCT_NAME: &IdentStr = ident_str!("UpgradeCap");
44pub const UPGRADETICKET_STRUCT_NAME: &IdentStr = ident_str!("UpgradeTicket");
45pub const UPGRADERECEIPT_STRUCT_NAME: &IdentStr = ident_str!("UpgradeReceipt");
46
47#[derive(Clone, Debug)]
48pub struct FnInfo {
50 pub is_test: bool,
53}
54
55#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
56pub struct FnInfoKey {
58 pub fn_name: String,
59 pub mod_addr: AccountAddress,
60}
61
62pub type FnInfoMap = BTreeMap<FnInfoKey, FnInfo>;
64
65#[derive(
67 Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, Hash, JsonSchema,
68)]
69pub struct TypeOrigin {
70 pub module_name: String,
71 #[serde(alias = "struct_name")]
73 pub datatype_name: String,
74 pub package: ObjectID,
75}
76
77#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash, JsonSchema)]
79pub struct UpgradeInfo {
80 pub upgraded_id: ObjectID,
82 pub upgraded_version: SequenceNumber,
84}
85
86#[serde_as]
89#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
90pub struct MovePackage {
91 pub(crate) id: ObjectID,
92 pub(crate) version: SequenceNumber,
104 #[serde_as(as = "BTreeMap<_, Bytes>")]
106 pub(crate) module_map: BTreeMap<String, Vec<u8>>,
107
108 pub(crate) type_origin_table: Vec<TypeOrigin>,
111
112 pub(crate) linkage_table: BTreeMap<ObjectID, UpgradeInfo>,
115}
116
117#[repr(u8)]
122#[derive(Display, Debug, Clone, Copy)]
123pub enum UpgradePolicy {
124 #[display("COMPATIBLE")]
125 Compatible = 0,
126 #[display("ADDITIVE")]
127 Additive = 128,
128 #[display("DEP_ONLY")]
129 DepOnly = 192,
130}
131
132impl UpgradePolicy {
133 pub const COMPATIBLE: u8 = Self::Compatible as u8;
135 pub const ADDITIVE: u8 = Self::Additive as u8;
136 pub const DEP_ONLY: u8 = Self::DepOnly as u8;
137
138 pub fn is_valid_policy(policy: &u8) -> bool {
139 Self::try_from(*policy).is_ok()
140 }
141}
142
143impl TryFrom<u8> for UpgradePolicy {
144 type Error = ();
145 fn try_from(value: u8) -> Result<Self, Self::Error> {
146 match value {
147 x if x == Self::Compatible as u8 => Ok(Self::Compatible),
148 x if x == Self::Additive as u8 => Ok(Self::Additive),
149 x if x == Self::DepOnly as u8 => Ok(Self::DepOnly),
150 _ => Err(()),
151 }
152 }
153}
154
155#[derive(Debug, Serialize, Deserialize)]
157pub struct UpgradeCap {
158 pub id: UID,
159 pub package: ID,
160 pub version: u64,
161 pub policy: u8,
162}
163
164#[derive(Debug, Serialize, Deserialize)]
166pub struct UpgradeTicket {
167 pub cap: ID,
168 pub package: ID,
169 pub policy: u8,
170 pub digest: Vec<u8>,
171}
172
173#[derive(Debug, Serialize, Deserialize)]
175pub struct UpgradeReceipt {
176 pub cap: ID,
177 pub package: ID,
178}
179
180impl MovePackage {
181 pub fn new(
184 id: ObjectID,
185 version: SequenceNumber,
186 module_map: BTreeMap<String, Vec<u8>>,
187 max_move_package_size: u64,
188 type_origin_table: Vec<TypeOrigin>,
189 linkage_table: BTreeMap<ObjectID, UpgradeInfo>,
190 ) -> Result<Self, ExecutionError> {
191 let pkg = Self {
192 id,
193 version,
194 module_map,
195 type_origin_table,
196 linkage_table,
197 };
198 let object_size = pkg.size() as u64;
199 if object_size > max_move_package_size {
200 return Err(ExecutionErrorKind::MovePackageTooBig {
201 object_size,
202 max_object_size: max_move_package_size,
203 }
204 .into());
205 }
206 Ok(pkg)
207 }
208
209 pub fn digest(&self) -> [u8; 32] {
210 Self::compute_digest_for_modules_and_deps(
211 self.module_map.values(),
212 self.linkage_table
213 .values()
214 .map(|UpgradeInfo { upgraded_id, .. }| upgraded_id),
215 )
216 }
217
218 pub fn compute_digest_for_modules_and_deps<'a>(
222 modules: impl IntoIterator<Item = &'a Vec<u8>>,
223 object_ids: impl IntoIterator<Item = &'a ObjectID>,
224 ) -> [u8; 32] {
225 let mut components = object_ids
226 .into_iter()
227 .map(|o| ***o)
228 .chain(
229 modules
230 .into_iter()
231 .map(|module| DefaultHash::digest(module).digest),
232 )
233 .collect::<Vec<_>>();
234
235 components.sort();
238
239 let mut digest = DefaultHash::default();
240 for c in components {
241 digest.update(c);
242 }
243 digest.finalize().digest
244 }
245
246 pub fn new_initial<'p>(
249 modules: &[CompiledModule],
250 max_move_package_size: u64,
251 move_binary_format_version: u32,
252 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
253 ) -> Result<Self, ExecutionError> {
254 let module = modules
255 .first()
256 .expect("Tried to build a Move package from an empty iterator of Compiled modules");
257 let runtime_id = ObjectID::from(*module.address());
258 let storage_id = runtime_id;
259 let type_origin_table = build_initial_type_origin_table(modules);
260 Self::from_module_iter_with_type_origin_table(
261 storage_id,
262 runtime_id,
263 OBJECT_START_VERSION,
264 modules,
265 max_move_package_size,
266 move_binary_format_version,
267 type_origin_table,
268 transitive_dependencies,
269 )
270 }
271
272 pub fn new_upgraded<'p>(
275 &self,
276 storage_id: ObjectID,
277 modules: &[CompiledModule],
278 protocol_config: &ProtocolConfig,
279 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
280 ) -> Result<Self, ExecutionError> {
281 let module = modules
282 .first()
283 .expect("Tried to build a Move package from an empty iterator of Compiled modules");
284 let runtime_id = ObjectID::from(*module.address());
285 let type_origin_table = build_upgraded_type_origin_table(self, modules, storage_id)?;
286 let mut new_version = self.version();
287 new_version.increment();
288 Self::from_module_iter_with_type_origin_table(
289 storage_id,
290 runtime_id,
291 new_version,
292 modules,
293 protocol_config.max_move_package_size(),
294 protocol_config.move_binary_format_version(),
295 type_origin_table,
296 transitive_dependencies,
297 )
298 }
299
300 pub fn new_system(
301 version: SequenceNumber,
302 modules: &[CompiledModule],
303 dependencies: impl IntoIterator<Item = ObjectID>,
304 ) -> Self {
305 let module = modules
306 .first()
307 .expect("Tried to build a Move package from an empty iterator of Compiled modules");
308
309 let storage_id = ObjectID::from(*module.address());
310 let type_origin_table = build_initial_type_origin_table(modules);
311
312 let linkage_table = BTreeMap::from_iter(dependencies.into_iter().map(|dep| {
313 let info = UpgradeInfo {
314 upgraded_id: dep,
315 upgraded_version: SequenceNumber::new(),
327 };
328 (dep, info)
329 }));
330
331 let module_map = BTreeMap::from_iter(modules.iter().map(|module| {
332 let name = module.name().to_string();
333 let mut bytes = Vec::new();
334 module
335 .serialize_with_version(module.version, &mut bytes)
336 .unwrap();
337 (name, bytes)
338 }));
339
340 Self::new(
341 storage_id,
342 version,
343 module_map,
344 u64::MAX, type_origin_table,
346 linkage_table,
347 )
348 .expect("System packages are not subject to a size limit")
349 }
350
351 fn from_module_iter_with_type_origin_table<'p>(
352 storage_id: ObjectID,
353 self_id: ObjectID,
354 version: SequenceNumber,
355 modules: &[CompiledModule],
356 max_move_package_size: u64,
357 move_binary_format_version: u32,
358 type_origin_table: Vec<TypeOrigin>,
359 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
360 ) -> Result<Self, ExecutionError> {
361 let mut module_map = BTreeMap::new();
362 let mut immediate_dependencies = BTreeSet::new();
363
364 for module in modules {
365 let name = module.name().to_string();
366
367 immediate_dependencies.extend(
368 module
369 .immediate_dependencies()
370 .into_iter()
371 .map(|dep| ObjectID::from(*dep.address())),
372 );
373
374 let mut bytes = Vec::new();
375 let version = if move_binary_format_version > VERSION_6 {
376 module.version
377 } else {
378 VERSION_6
379 };
380 module.serialize_with_version(version, &mut bytes).unwrap();
381 module_map.insert(name, bytes);
382 }
383
384 immediate_dependencies.remove(&self_id);
385 let linkage_table = build_linkage_table(immediate_dependencies, transitive_dependencies)?;
386 Self::new(
387 storage_id,
388 version,
389 module_map,
390 max_move_package_size,
391 type_origin_table,
392 linkage_table,
393 )
394 }
395
396 pub fn get_module(&self, storage_id: &ModuleId) -> Option<&Vec<u8>> {
402 if self.id != ObjectID::from(*storage_id.address()) {
403 None
404 } else {
405 self.module_map.get(&storage_id.name().to_string())
406 }
407 }
408
409 pub fn size(&self) -> usize {
411 let module_map_size = self
412 .module_map
413 .iter()
414 .map(|(name, module)| name.len() + module.len())
415 .sum::<usize>();
416 let type_origin_table_size = self
417 .type_origin_table
418 .iter()
419 .map(
420 |TypeOrigin {
421 module_name,
422 datatype_name: struct_name,
423 ..
424 }| module_name.len() + struct_name.len() + ObjectID::LENGTH,
425 )
426 .sum::<usize>();
427
428 let linkage_table_size = self.linkage_table.len()
429 * (ObjectID::LENGTH
430 + (
431 ObjectID::LENGTH + 8
432 ));
434
435 8 + module_map_size + type_origin_table_size + linkage_table_size
436 }
437
438 pub fn id(&self) -> ObjectID {
439 self.id
440 }
441
442 pub fn version(&self) -> SequenceNumber {
443 self.version
444 }
445
446 pub fn decrement_version(&mut self) {
447 self.version.decrement();
448 }
449
450 pub fn increment_version(&mut self) {
451 self.version.increment();
452 }
453
454 pub fn object_size_for_gas_metering(&self) -> usize {
456 self.size()
457 }
458
459 pub fn serialized_module_map(&self) -> &BTreeMap<String, Vec<u8>> {
460 &self.module_map
461 }
462
463 pub fn type_origin_table(&self) -> &Vec<TypeOrigin> {
464 &self.type_origin_table
465 }
466
467 pub fn type_origin_map(&self) -> BTreeMap<(String, String), ObjectID> {
468 self.type_origin_table
469 .iter()
470 .map(
471 |TypeOrigin {
472 module_name,
473 datatype_name: struct_name,
474 package,
475 }| { ((module_name.clone(), struct_name.clone()), *package) },
476 )
477 .collect()
478 }
479
480 pub fn linkage_table(&self) -> &BTreeMap<ObjectID, UpgradeInfo> {
481 &self.linkage_table
482 }
483
484 pub fn original_package_id(&self) -> ObjectID {
488 if self.version == OBJECT_START_VERSION {
489 return self.id;
491 }
492
493 let bytes = self.module_map.values().next().expect("Empty module map");
494 let module = CompiledModule::deserialize_with_defaults(bytes)
495 .expect("A Move package contains a module that cannot be deserialized");
496 (*module.address()).into()
497 }
498
499 pub fn deserialize_module(
500 &self,
501 module: &Identifier,
502 binary_config: &BinaryConfig,
503 ) -> IotaResult<CompiledModule> {
504 let bytes = self
506 .serialized_module_map()
507 .get(module.as_str())
508 .ok_or_else(|| IotaError::ModuleNotFound {
509 module_name: module.to_string(),
510 })?;
511 CompiledModule::deserialize_with_config(bytes, binary_config).map_err(|error| {
512 IotaError::ModuleDeserializationFailure {
513 error: error.to_string(),
514 }
515 })
516 }
517
518 pub fn disassemble(&self) -> IotaResult<BTreeMap<String, Value>> {
519 disassemble_modules(self.module_map.values())
520 }
521
522 pub fn normalize(
523 &self,
524 binary_config: &BinaryConfig,
525 ) -> IotaResult<BTreeMap<String, normalized::Module>> {
526 normalize_modules(self.module_map.values(), binary_config)
527 }
528}
529
530impl UpgradeCap {
531 pub fn type_() -> StructTag {
532 StructTag {
533 address: IOTA_FRAMEWORK_ADDRESS,
534 module: PACKAGE_MODULE_NAME.to_owned(),
535 name: UPGRADECAP_STRUCT_NAME.to_owned(),
536 type_params: vec![],
537 }
538 }
539
540 pub fn new(uid: ObjectID, package_id: ObjectID) -> Self {
543 UpgradeCap {
544 id: UID::new(uid),
545 package: ID::new(package_id),
546 version: 1,
547 policy: UpgradePolicy::COMPATIBLE,
548 }
549 }
550}
551
552impl UpgradeTicket {
553 pub fn type_() -> StructTag {
554 StructTag {
555 address: IOTA_FRAMEWORK_ADDRESS,
556 module: PACKAGE_MODULE_NAME.to_owned(),
557 name: UPGRADETICKET_STRUCT_NAME.to_owned(),
558 type_params: vec![],
559 }
560 }
561}
562
563impl UpgradeReceipt {
564 pub fn type_() -> StructTag {
565 StructTag {
566 address: IOTA_FRAMEWORK_ADDRESS,
567 module: PACKAGE_MODULE_NAME.to_owned(),
568 name: UPGRADERECEIPT_STRUCT_NAME.to_owned(),
569 type_params: vec![],
570 }
571 }
572
573 pub fn new(upgrade_ticket: UpgradeTicket, upgraded_package_id: ObjectID) -> Self {
576 UpgradeReceipt {
577 cap: upgrade_ticket.cap,
578 package: ID::new(upgraded_package_id),
579 }
580 }
581}
582
583pub fn is_test_fun(name: &IdentStr, module: &CompiledModule, fn_info_map: &FnInfoMap) -> bool {
585 let fn_name = name.to_string();
586 let mod_handle = module.self_handle();
587 let mod_addr = *module.address_identifier_at(mod_handle.address);
588 let fn_info_key = FnInfoKey { fn_name, mod_addr };
589 match fn_info_map.get(&fn_info_key) {
590 Some(fn_info) => fn_info.is_test,
591 None => false,
592 }
593}
594
595pub fn disassemble_modules<'a, I>(modules: I) -> IotaResult<BTreeMap<String, Value>>
596where
597 I: Iterator<Item = &'a Vec<u8>>,
598{
599 let mut disassembled = BTreeMap::new();
600 for bytecode in modules {
601 let module = CompiledModule::deserialize_with_defaults(bytecode).map_err(|error| {
604 IotaError::ModuleDeserializationFailure {
605 error: error.to_string(),
606 }
607 })?;
608 let d =
609 Disassembler::from_module(&module, Spanned::unsafe_no_loc(()).loc).map_err(|e| {
610 IotaError::ObjectSerialization {
611 error: e.to_string(),
612 }
613 })?;
614 let bytecode_str = d
615 .disassemble()
616 .map_err(|e| IotaError::ObjectSerialization {
617 error: e.to_string(),
618 })?;
619 disassembled.insert(module.name().to_string(), Value::String(bytecode_str));
620 }
621 Ok(disassembled)
622}
623
624pub fn normalize_modules<'a, I>(
625 modules: I,
626 binary_config: &BinaryConfig,
627) -> IotaResult<BTreeMap<String, normalized::Module>>
628where
629 I: Iterator<Item = &'a Vec<u8>>,
630{
631 let mut normalized_modules = BTreeMap::new();
632 for bytecode in modules {
633 let module =
634 CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
635 IotaError::ModuleDeserializationFailure {
636 error: error.to_string(),
637 }
638 })?;
639 let normalized_module = normalized::Module::new(&module);
640 normalized_modules.insert(normalized_module.name.to_string(), normalized_module);
641 }
642 Ok(normalized_modules)
643}
644
645pub fn normalize_deserialized_modules<'a, I>(modules: I) -> BTreeMap<String, normalized::Module>
646where
647 I: Iterator<Item = &'a CompiledModule>,
648{
649 let mut normalized_modules = BTreeMap::new();
650 for module in modules {
651 let normalized_module = normalized::Module::new(module);
652 normalized_modules.insert(normalized_module.name.to_string(), normalized_module);
653 }
654 normalized_modules
655}
656
657fn build_linkage_table<'p>(
658 mut immediate_dependencies: BTreeSet<ObjectID>,
659 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
660) -> Result<BTreeMap<ObjectID, UpgradeInfo>, ExecutionError> {
661 let mut linkage_table = BTreeMap::new();
662 let mut dep_linkage_tables = vec![];
663
664 for transitive_dep in transitive_dependencies.into_iter() {
665 let original_id = transitive_dep.original_package_id();
669
670 if immediate_dependencies.remove(&original_id) {
671 dep_linkage_tables.push(&transitive_dep.linkage_table);
674 }
675
676 linkage_table.insert(
677 original_id,
678 UpgradeInfo {
679 upgraded_id: transitive_dep.id,
680 upgraded_version: transitive_dep.version,
681 },
682 );
683 }
684 if !immediate_dependencies.is_empty() {
686 return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
687 }
688
689 for dep_linkage_table in dep_linkage_tables {
691 for (original_id, dep_info) in dep_linkage_table {
692 let Some(our_info) = linkage_table.get(original_id) else {
693 return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
694 };
695
696 if our_info.upgraded_version < dep_info.upgraded_version {
697 return Err(ExecutionErrorKind::PublishUpgradeDependencyDowngrade.into());
698 }
699 }
700 }
701
702 Ok(linkage_table)
703}
704
705fn build_initial_type_origin_table(modules: &[CompiledModule]) -> Vec<TypeOrigin> {
706 modules
707 .iter()
708 .flat_map(|m| {
709 m.struct_defs()
710 .iter()
711 .map(|struct_def| {
712 let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
713 let module_name = m.name().to_string();
714 let struct_name = m.identifier_at(struct_handle.name).to_string();
715 let package: ObjectID = (*m.self_id().address()).into();
716 TypeOrigin {
717 module_name,
718 datatype_name: struct_name,
719 package,
720 }
721 })
722 .chain(m.enum_defs().iter().map(|enum_def| {
723 let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
724 let module_name = m.name().to_string();
725 let enum_name = m.identifier_at(enum_handle.name).to_string();
726 let package: ObjectID = (*m.self_id().address()).into();
727 TypeOrigin {
728 module_name,
729 datatype_name: enum_name,
730 package,
731 }
732 }))
733 })
734 .collect()
735}
736
737fn build_upgraded_type_origin_table(
738 predecessor: &MovePackage,
739 modules: &[CompiledModule],
740 storage_id: ObjectID,
741) -> Result<Vec<TypeOrigin>, ExecutionError> {
742 let mut new_table = vec![];
743 let mut existing_table = predecessor.type_origin_map();
744 for m in modules {
745 for struct_def in m.struct_defs() {
746 let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
747 let module_name = m.name().to_string();
748 let struct_name = m.identifier_at(struct_handle.name).to_string();
749 let mod_key = (module_name.clone(), struct_name.clone());
750 let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
753 new_table.push(TypeOrigin {
754 module_name,
755 datatype_name: struct_name,
756 package,
757 });
758 }
759
760 for enum_def in m.enum_defs() {
761 let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
762 let module_name = m.name().to_string();
763 let enum_name = m.identifier_at(enum_handle.name).to_string();
764 let mod_key = (module_name.clone(), enum_name.clone());
765 let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
768 new_table.push(TypeOrigin {
769 module_name,
770 datatype_name: enum_name,
771 package,
772 });
773 }
774 }
775
776 if !existing_table.is_empty() {
777 Err(ExecutionError::from_kind(
778 ExecutionErrorKind::PackageUpgradeError {
779 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
780 },
781 ))
782 } else {
783 Ok(new_table)
784 }
785}