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