1use std::{
6 collections::{BTreeMap, BTreeSet},
7 hash::Hash,
8};
9
10use derive_more::Display;
11use fastcrypto::hash::HashFunction;
12use iota_protocol_config::ProtocolConfig;
13use move_binary_format::{
14 binary_config::BinaryConfig, file_format::CompiledModule, file_format_common::VERSION_6,
15 normalized,
16};
17use move_core_types::{
18 account_address::AccountAddress,
19 ident_str,
20 identifier::{IdentStr, Identifier},
21 language_storage::{ModuleId, StructTag},
22};
23use schemars::JsonSchema;
24use serde::{Deserialize, Serialize};
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 protocol_config: &ProtocolConfig,
251 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
252 ) -> Result<Self, 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::from(*module.address());
257 let storage_id = runtime_id;
258 let type_origin_table = build_initial_type_origin_table(modules);
259 Self::from_module_iter_with_type_origin_table(
260 storage_id,
261 runtime_id,
262 OBJECT_START_VERSION,
263 modules,
264 protocol_config,
265 type_origin_table,
266 transitive_dependencies,
267 )
268 }
269
270 pub fn new_upgraded<'p>(
273 &self,
274 storage_id: ObjectID,
275 modules: &[CompiledModule],
276 protocol_config: &ProtocolConfig,
277 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
278 ) -> Result<Self, ExecutionError> {
279 let module = modules
280 .first()
281 .expect("Tried to build a Move package from an empty iterator of Compiled modules");
282 let runtime_id = ObjectID::from(*module.address());
283 let type_origin_table = build_upgraded_type_origin_table(self, modules, storage_id)?;
284 let mut new_version = self.version();
285 new_version.increment();
286 Self::from_module_iter_with_type_origin_table(
287 storage_id,
288 runtime_id,
289 new_version,
290 modules,
291 protocol_config,
292 type_origin_table,
293 transitive_dependencies,
294 )
295 }
296
297 pub fn new_system(
298 version: SequenceNumber,
299 modules: &[CompiledModule],
300 dependencies: impl IntoIterator<Item = ObjectID>,
301 ) -> Self {
302 let module = modules
303 .first()
304 .expect("Tried to build a Move package from an empty iterator of Compiled modules");
305
306 let storage_id = ObjectID::from(*module.address());
307 let type_origin_table = build_initial_type_origin_table(modules);
308
309 let linkage_table = BTreeMap::from_iter(dependencies.into_iter().map(|dep| {
310 let info = UpgradeInfo {
311 upgraded_id: dep,
312 upgraded_version: SequenceNumber::new(),
324 };
325 (dep, info)
326 }));
327
328 let module_map = BTreeMap::from_iter(modules.iter().map(|module| {
329 let name = module.name().to_string();
330 let mut bytes = Vec::new();
331 module
332 .serialize_with_version(module.version, &mut bytes)
333 .unwrap();
334 (name, bytes)
335 }));
336
337 Self::new(
338 storage_id,
339 version,
340 module_map,
341 u64::MAX, type_origin_table,
343 linkage_table,
344 )
345 .expect("System packages are not subject to a size limit")
346 }
347
348 fn from_module_iter_with_type_origin_table<'p>(
349 storage_id: ObjectID,
350 self_id: ObjectID,
351 version: SequenceNumber,
352 modules: &[CompiledModule],
353 protocol_config: &ProtocolConfig,
354 type_origin_table: Vec<TypeOrigin>,
355 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
356 ) -> Result<Self, ExecutionError> {
357 let mut module_map = BTreeMap::new();
358 let mut immediate_dependencies = BTreeSet::new();
359
360 for module in modules {
361 let name = module.name().to_string();
362
363 immediate_dependencies.extend(
364 module
365 .immediate_dependencies()
366 .into_iter()
367 .map(|dep| ObjectID::from(*dep.address())),
368 );
369
370 let mut bytes = Vec::new();
371 let version = if protocol_config.move_binary_format_version() > VERSION_6 {
372 module.version
373 } else {
374 VERSION_6
375 };
376 module.serialize_with_version(version, &mut bytes).unwrap();
377 module_map.insert(name, bytes);
378 }
379
380 immediate_dependencies.remove(&self_id);
381 let linkage_table = build_linkage_table(
382 immediate_dependencies,
383 transitive_dependencies,
384 protocol_config,
385 )?;
386 Self::new(
387 storage_id,
388 version,
389 module_map,
390 protocol_config.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 pub fn normalize<S: Hash + Eq + Clone + ToString, Pool: normalized::StringPool<String = S>>(
520 &self,
521 pool: &mut Pool,
522 binary_config: &BinaryConfig,
523 include_code: bool,
524 ) -> IotaResult<BTreeMap<String, normalized::Module<S>>> {
525 normalize_modules(pool, self.module_map.values(), binary_config, include_code)
526 }
527}
528
529impl UpgradeCap {
530 pub fn type_() -> StructTag {
531 StructTag {
532 address: IOTA_FRAMEWORK_ADDRESS,
533 module: PACKAGE_MODULE_NAME.to_owned(),
534 name: UPGRADECAP_STRUCT_NAME.to_owned(),
535 type_params: vec![],
536 }
537 }
538
539 pub fn new(uid: ObjectID, package_id: ObjectID) -> Self {
542 UpgradeCap {
543 id: UID::new(uid),
544 package: ID::new(package_id),
545 version: 1,
546 policy: UpgradePolicy::COMPATIBLE,
547 }
548 }
549}
550
551impl UpgradeTicket {
552 pub fn type_() -> StructTag {
553 StructTag {
554 address: IOTA_FRAMEWORK_ADDRESS,
555 module: PACKAGE_MODULE_NAME.to_owned(),
556 name: UPGRADETICKET_STRUCT_NAME.to_owned(),
557 type_params: vec![],
558 }
559 }
560}
561
562impl UpgradeReceipt {
563 pub fn type_() -> StructTag {
564 StructTag {
565 address: IOTA_FRAMEWORK_ADDRESS,
566 module: PACKAGE_MODULE_NAME.to_owned(),
567 name: UPGRADERECEIPT_STRUCT_NAME.to_owned(),
568 type_params: vec![],
569 }
570 }
571
572 pub fn new(upgrade_ticket: UpgradeTicket, upgraded_package_id: ObjectID) -> Self {
575 UpgradeReceipt {
576 cap: upgrade_ticket.cap,
577 package: ID::new(upgraded_package_id),
578 }
579 }
580}
581
582pub fn is_test_fun(name: &IdentStr, module: &CompiledModule, fn_info_map: &FnInfoMap) -> bool {
584 let fn_name = name.to_string();
585 let mod_handle = module.self_handle();
586 let mod_addr = *module.address_identifier_at(mod_handle.address);
587 let fn_info_key = FnInfoKey { fn_name, mod_addr };
588 match fn_info_map.get(&fn_info_key) {
589 Some(fn_info) => fn_info.is_test,
590 None => false,
591 }
592}
593
594pub fn normalize_modules<
597 'a,
598 S: Hash + Eq + Clone + ToString,
599 Pool: normalized::StringPool<String = S>,
600 I,
601>(
602 pool: &mut Pool,
603 modules: I,
604 binary_config: &BinaryConfig,
605 include_code: bool,
606) -> IotaResult<BTreeMap<String, normalized::Module<S>>>
607where
608 I: Iterator<Item = &'a Vec<u8>>,
609{
610 let mut normalized_modules = BTreeMap::new();
611 for bytecode in modules {
612 let module =
613 CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
614 IotaError::ModuleDeserializationFailure {
615 error: error.to_string(),
616 }
617 })?;
618 let normalized_module = normalized::Module::new(pool, &module, include_code);
619 normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
620 }
621 Ok(normalized_modules)
622}
623
624pub fn normalize_deserialized_modules<
627 'a,
628 S: Hash + Eq + Clone + ToString,
629 Pool: normalized::StringPool<String = S>,
630 I,
631>(
632 pool: &mut Pool,
633 modules: I,
634 include_code: bool,
635) -> BTreeMap<String, normalized::Module<S>>
636where
637 I: Iterator<Item = &'a CompiledModule>,
638{
639 let mut normalized_modules = BTreeMap::new();
640 for module in modules {
641 let normalized_module = normalized::Module::new(pool, module, include_code);
642 normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
643 }
644 normalized_modules
645}
646
647fn build_linkage_table<'p>(
648 mut immediate_dependencies: BTreeSet<ObjectID>,
649 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
650 protocol_config: &ProtocolConfig,
651) -> Result<BTreeMap<ObjectID, UpgradeInfo>, ExecutionError> {
652 let mut linkage_table = BTreeMap::new();
653 let mut dep_linkage_tables = vec![];
654
655 for transitive_dep in transitive_dependencies.into_iter() {
656 let original_id = transitive_dep.original_package_id();
660
661 let imm_dep = immediate_dependencies.remove(&original_id);
662
663 if protocol_config.dependency_linkage_error() {
664 dep_linkage_tables.push(&transitive_dep.linkage_table);
665
666 let existing = linkage_table.insert(
667 original_id,
668 UpgradeInfo {
669 upgraded_id: transitive_dep.id,
670 upgraded_version: transitive_dep.version,
671 },
672 );
673
674 if existing.is_some() {
675 return Err(ExecutionErrorKind::InvalidLinkage.into());
676 }
677 } else {
678 if imm_dep {
679 dep_linkage_tables.push(&transitive_dep.linkage_table);
682 }
683 linkage_table.insert(
684 original_id,
685 UpgradeInfo {
686 upgraded_id: transitive_dep.id,
687 upgraded_version: transitive_dep.version,
688 },
689 );
690 }
691 }
692 if !immediate_dependencies.is_empty() {
694 return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
695 }
696
697 for dep_linkage_table in dep_linkage_tables {
699 for (original_id, dep_info) in dep_linkage_table {
700 let Some(our_info) = linkage_table.get(original_id) else {
701 return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
702 };
703
704 if our_info.upgraded_version < dep_info.upgraded_version {
705 return Err(ExecutionErrorKind::PublishUpgradeDependencyDowngrade.into());
706 }
707 }
708 }
709
710 Ok(linkage_table)
711}
712
713fn build_initial_type_origin_table(modules: &[CompiledModule]) -> Vec<TypeOrigin> {
714 modules
715 .iter()
716 .flat_map(|m| {
717 m.struct_defs()
718 .iter()
719 .map(|struct_def| {
720 let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
721 let module_name = m.name().to_string();
722 let struct_name = m.identifier_at(struct_handle.name).to_string();
723 let package: ObjectID = (*m.self_id().address()).into();
724 TypeOrigin {
725 module_name,
726 datatype_name: struct_name,
727 package,
728 }
729 })
730 .chain(m.enum_defs().iter().map(|enum_def| {
731 let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
732 let module_name = m.name().to_string();
733 let enum_name = m.identifier_at(enum_handle.name).to_string();
734 let package: ObjectID = (*m.self_id().address()).into();
735 TypeOrigin {
736 module_name,
737 datatype_name: enum_name,
738 package,
739 }
740 }))
741 })
742 .collect()
743}
744
745fn build_upgraded_type_origin_table(
746 predecessor: &MovePackage,
747 modules: &[CompiledModule],
748 storage_id: ObjectID,
749) -> Result<Vec<TypeOrigin>, ExecutionError> {
750 let mut new_table = vec![];
751 let mut existing_table = predecessor.type_origin_map();
752 for m in modules {
753 for struct_def in m.struct_defs() {
754 let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
755 let module_name = m.name().to_string();
756 let struct_name = m.identifier_at(struct_handle.name).to_string();
757 let mod_key = (module_name.clone(), struct_name.clone());
758 let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
761 new_table.push(TypeOrigin {
762 module_name,
763 datatype_name: struct_name,
764 package,
765 });
766 }
767
768 for enum_def in m.enum_defs() {
769 let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
770 let module_name = m.name().to_string();
771 let enum_name = m.identifier_at(enum_handle.name).to_string();
772 let mod_key = (module_name.clone(), enum_name.clone());
773 let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
776 new_table.push(TypeOrigin {
777 module_name,
778 datatype_name: enum_name,
779 package,
780 });
781 }
782 }
783
784 if !existing_table.is_empty() {
785 Err(ExecutionError::from_kind(
786 ExecutionErrorKind::PackageUpgradeError {
787 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
788 },
789 ))
790 } else {
791 Ok(new_table)
792 }
793}