1use alloc::{
4 collections::{BTreeMap, BTreeSet},
5 format,
6 string::String,
7 vec::Vec,
8};
9use core::{
10 convert::TryFrom,
11 fmt::{self, Debug, Display, Formatter},
12};
13
14#[cfg(any(feature = "testing", feature = "gens", test))]
15use rand::{distributions::Standard, prelude::Distribution, Rng};
16
17#[cfg(feature = "datasize")]
18use datasize::DataSize;
19#[cfg(feature = "json-schema")]
20use schemars::JsonSchema;
21use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
22#[cfg(feature = "json-schema")]
23use serde_map_to_array::KeyValueJsonSchema;
24use serde_map_to_array::{BTreeMapToArray, KeyValueLabels};
25
26use crate::{
27 addressable_entity::{Error, FromStrError},
28 bytesrepr::{self, FromBytes, ToBytes, U32_SERIALIZED_LENGTH},
29 checksummed_hex,
30 crypto::{self, PublicKey},
31 uref::URef,
32 CLType, CLTyped, EntityAddr, HashAddr, BLAKE2B_DIGEST_LENGTH, KEY_HASH_LENGTH,
33};
34
35const PACKAGE_STRING_PREFIX: &str = "package-";
36
37#[derive(Debug)]
39pub struct TryFromSliceForPackageHashError(());
40
41impl Display for TryFromSliceForPackageHashError {
42 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
43 write!(f, "failed to retrieve from slice")
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
50#[cfg_attr(feature = "datasize", derive(DataSize))]
51#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
52pub struct Group(String);
53
54impl Group {
55 pub fn new<T: Into<String>>(s: T) -> Self {
57 Group(s.into())
58 }
59
60 pub fn value(&self) -> &str {
62 &self.0
63 }
64}
65
66impl From<Group> for String {
67 fn from(group: Group) -> Self {
68 group.0
69 }
70}
71
72impl ToBytes for Group {
73 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
74 self.0.to_bytes()
75 }
76
77 fn serialized_length(&self) -> usize {
78 self.0.serialized_length()
79 }
80
81 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
82 self.value().write_bytes(writer)?;
83 Ok(())
84 }
85}
86
87impl FromBytes for Group {
88 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
89 String::from_bytes(bytes).map(|(label, bytes)| (Group(label), bytes))
90 }
91}
92
93pub type EntityVersion = u32;
95
96pub const ENTITY_INITIAL_VERSION: EntityVersion = 1;
98
99pub type ProtocolVersionMajor = u32;
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
104#[cfg_attr(feature = "datasize", derive(DataSize))]
105#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
106pub struct EntityVersionKey {
107 protocol_version_major: ProtocolVersionMajor,
109 entity_version: EntityVersion,
111}
112
113impl EntityVersionKey {
114 pub fn new(
116 protocol_version_major: ProtocolVersionMajor,
117 entity_version: EntityVersion,
118 ) -> Self {
119 Self {
120 protocol_version_major,
121 entity_version,
122 }
123 }
124
125 pub fn protocol_version_major(self) -> ProtocolVersionMajor {
127 self.protocol_version_major
128 }
129
130 pub fn entity_version(self) -> EntityVersion {
132 self.entity_version
133 }
134}
135
136impl From<EntityVersionKey> for (ProtocolVersionMajor, EntityVersion) {
137 fn from(entity_version_key: EntityVersionKey) -> Self {
138 (
139 entity_version_key.protocol_version_major,
140 entity_version_key.entity_version,
141 )
142 }
143}
144
145impl ToBytes for EntityVersionKey {
146 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
147 let mut buffer = bytesrepr::allocate_buffer(self)?;
148 self.write_bytes(&mut buffer)?;
149 Ok(buffer)
150 }
151
152 fn serialized_length(&self) -> usize {
153 ENTITY_VERSION_KEY_SERIALIZED_LENGTH
154 }
155
156 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
157 self.protocol_version_major.write_bytes(writer)?;
158 self.entity_version.write_bytes(writer)
159 }
160}
161
162impl FromBytes for EntityVersionKey {
163 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
164 let (protocol_version_major, remainder) = ProtocolVersionMajor::from_bytes(bytes)?;
165 let (entity_version, remainder) = EntityVersion::from_bytes(remainder)?;
166 Ok((
167 EntityVersionKey {
168 protocol_version_major,
169 entity_version,
170 },
171 remainder,
172 ))
173 }
174}
175
176impl Display for EntityVersionKey {
177 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
178 write!(f, "{}.{}", self.protocol_version_major, self.entity_version)
179 }
180}
181
182#[cfg(any(feature = "testing", feature = "gens", test))]
183impl Distribution<EntityVersionKey> for Standard {
184 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> EntityVersionKey {
185 EntityVersionKey {
186 protocol_version_major: rng.gen(),
187 entity_version: rng.gen(),
188 }
189 }
190}
191
192pub const ENTITY_VERSION_KEY_SERIALIZED_LENGTH: usize =
194 U32_SERIALIZED_LENGTH + U32_SERIALIZED_LENGTH;
195
196#[derive(Clone, PartialEq, Eq, Default, Serialize, Deserialize, Debug)]
198#[cfg_attr(feature = "datasize", derive(DataSize))]
199#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
200#[serde(transparent, deny_unknown_fields)]
201pub struct EntityVersions(
202 #[serde(with = "BTreeMapToArray::<EntityVersionKey, EntityAddr, EntityVersionLabels>")]
203 BTreeMap<EntityVersionKey, EntityAddr>,
204);
205
206impl EntityVersions {
207 pub const fn new() -> Self {
209 EntityVersions(BTreeMap::new())
210 }
211
212 pub fn contract_hashes(&self) -> impl Iterator<Item = &EntityAddr> {
214 self.0.values()
215 }
216
217 pub fn get(&self, key: &EntityVersionKey) -> Option<&EntityAddr> {
219 self.0.get(key)
220 }
221
222 pub fn maybe_first(&mut self) -> Option<(EntityVersionKey, EntityAddr)> {
224 if let Some((entity_version_key, entity_hash)) = self.0.iter().next() {
225 Some((*entity_version_key, *entity_hash))
226 } else {
227 None
228 }
229 }
230
231 pub fn version_count(&self) -> usize {
233 self.0.len()
234 }
235
236 pub fn latest(&self) -> Option<&EntityAddr> {
238 let (_, value) = self.0.last_key_value()?;
239 Some(value)
240 }
241}
242
243impl ToBytes for EntityVersions {
244 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
245 self.0.to_bytes()
246 }
247
248 fn serialized_length(&self) -> usize {
249 self.0.serialized_length()
250 }
251
252 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
253 self.0.write_bytes(writer)
254 }
255}
256
257impl FromBytes for EntityVersions {
258 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
259 let (versions, remainder) = BTreeMap::<EntityVersionKey, EntityAddr>::from_bytes(bytes)?;
260 Ok((EntityVersions(versions), remainder))
261 }
262}
263
264impl From<BTreeMap<EntityVersionKey, EntityAddr>> for EntityVersions {
265 fn from(value: BTreeMap<EntityVersionKey, EntityAddr>) -> Self {
266 EntityVersions(value)
267 }
268}
269
270struct EntityVersionLabels;
271
272impl KeyValueLabels for EntityVersionLabels {
273 const KEY: &'static str = "entity_version_key";
274 const VALUE: &'static str = "entity_addr";
275}
276
277#[cfg(feature = "json-schema")]
278impl KeyValueJsonSchema for EntityVersionLabels {
279 const JSON_SCHEMA_KV_NAME: Option<&'static str> = Some("EntityVersionAndEntityAddr");
280}
281
282#[derive(Clone, PartialEq, Eq, Default, Serialize, Deserialize, Debug)]
284#[cfg_attr(feature = "datasize", derive(DataSize))]
285#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
286#[serde(transparent, deny_unknown_fields)]
287pub struct Groups(
288 #[serde(with = "BTreeMapToArray::<Group, BTreeSet::<URef>, GroupLabels>")]
289 pub(crate) BTreeMap<Group, BTreeSet<URef>>,
290);
291
292impl Groups {
293 pub const fn new() -> Self {
295 Groups(BTreeMap::new())
296 }
297
298 pub fn insert(&mut self, name: Group, urefs: BTreeSet<URef>) -> Option<BTreeSet<URef>> {
303 self.0.insert(name, urefs)
304 }
305
306 pub fn contains(&self, name: &Group) -> bool {
308 self.0.contains_key(name)
309 }
310
311 pub fn get(&self, name: &Group) -> Option<&BTreeSet<URef>> {
313 self.0.get(name)
314 }
315
316 pub fn get_mut(&mut self, name: &Group) -> Option<&mut BTreeSet<URef>> {
318 self.0.get_mut(name)
319 }
320
321 pub fn len(&self) -> usize {
323 self.0.len()
324 }
325
326 pub fn is_empty(&self) -> bool {
328 self.0.is_empty()
329 }
330
331 pub fn keys(&self) -> impl Iterator<Item = &BTreeSet<URef>> {
333 self.0.values()
334 }
335
336 pub fn total_urefs(&self) -> usize {
338 self.0.values().map(|urefs| urefs.len()).sum()
339 }
340}
341
342impl ToBytes for Groups {
343 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
344 self.0.to_bytes()
345 }
346
347 fn serialized_length(&self) -> usize {
348 self.0.serialized_length()
349 }
350
351 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
352 self.0.write_bytes(writer)
353 }
354}
355
356impl FromBytes for Groups {
357 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
358 let (groups, remainder) = BTreeMap::<Group, BTreeSet<URef>>::from_bytes(bytes)?;
359 Ok((Groups(groups), remainder))
360 }
361}
362
363struct GroupLabels;
364
365impl KeyValueLabels for GroupLabels {
366 const KEY: &'static str = "group_name";
367 const VALUE: &'static str = "group_users";
368}
369
370#[cfg(feature = "json-schema")]
371impl KeyValueJsonSchema for GroupLabels {
372 const JSON_SCHEMA_KV_NAME: Option<&'static str> = Some("NamedUserGroup");
373}
374
375#[cfg(any(feature = "testing", feature = "gens", test))]
376impl From<BTreeMap<Group, BTreeSet<URef>>> for Groups {
377 fn from(value: BTreeMap<Group, BTreeSet<URef>>) -> Self {
378 Groups(value)
379 }
380}
381
382#[derive(Default, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
384#[cfg_attr(feature = "datasize", derive(DataSize))]
385#[cfg_attr(
386 feature = "json-schema",
387 derive(JsonSchema),
388 schemars(description = "The hex-encoded address of the Package.")
389)]
390pub struct PackageHash(
391 #[cfg_attr(feature = "json-schema", schemars(skip, with = "String"))] HashAddr,
392);
393
394impl PackageHash {
395 pub const fn new(value: HashAddr) -> PackageHash {
397 PackageHash(value)
398 }
399
400 pub fn value(&self) -> HashAddr {
402 self.0
403 }
404
405 pub fn as_bytes(&self) -> &[u8] {
407 &self.0
408 }
409
410 pub fn to_formatted_string(self) -> String {
412 format!("{}{}", PACKAGE_STRING_PREFIX, base16::encode_lower(&self.0),)
413 }
414
415 pub fn from_formatted_str(input: &str) -> Result<Self, FromStrError> {
418 let hex_addr = input
419 .strip_prefix(PACKAGE_STRING_PREFIX)
420 .ok_or(FromStrError::InvalidPrefix)?;
421
422 let bytes = HashAddr::try_from(checksummed_hex::decode(hex_addr)?.as_ref())?;
423 Ok(PackageHash(bytes))
424 }
425
426 pub fn from_public_key(
428 public_key: &PublicKey,
429 blake2b_hash_fn: impl Fn(Vec<u8>) -> [u8; BLAKE2B_DIGEST_LENGTH],
430 ) -> Self {
431 const SYSTEM_LOWERCASE: &str = "system";
432 const ED25519_LOWERCASE: &str = "ed25519";
433 const SECP256K1_LOWERCASE: &str = "secp256k1";
434
435 let algorithm_name = match public_key {
436 PublicKey::System => SYSTEM_LOWERCASE,
437 PublicKey::Ed25519(_) => ED25519_LOWERCASE,
438 PublicKey::Secp256k1(_) => SECP256K1_LOWERCASE,
439 };
440 let public_key_bytes: Vec<u8> = public_key.into();
441
442 let preimage = {
444 let mut data = Vec::with_capacity(algorithm_name.len() + public_key_bytes.len() + 1);
445 data.extend(algorithm_name.as_bytes());
446 data.push(0);
447 data.extend(public_key_bytes);
448 data
449 };
450 let digest = blake2b_hash_fn(preimage);
452 Self::new(digest)
453 }
454}
455
456impl Display for PackageHash {
457 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
458 write!(f, "{}", base16::encode_lower(&self.0))
459 }
460}
461
462impl Debug for PackageHash {
463 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
464 write!(f, "PackageHash({})", base16::encode_lower(&self.0))
465 }
466}
467
468impl CLTyped for PackageHash {
469 fn cl_type() -> CLType {
470 CLType::ByteArray(KEY_HASH_LENGTH as u32)
471 }
472}
473
474impl ToBytes for PackageHash {
475 #[inline(always)]
476 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
477 self.0.to_bytes()
478 }
479
480 #[inline(always)]
481 fn serialized_length(&self) -> usize {
482 self.0.serialized_length()
483 }
484
485 #[inline(always)]
486 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
487 writer.extend_from_slice(&self.0);
488 Ok(())
489 }
490}
491
492impl FromBytes for PackageHash {
493 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
494 let (bytes, rem) = FromBytes::from_bytes(bytes)?;
495 Ok((PackageHash::new(bytes), rem))
496 }
497}
498
499impl From<[u8; 32]> for PackageHash {
500 fn from(bytes: [u8; 32]) -> Self {
501 PackageHash(bytes)
502 }
503}
504
505impl Serialize for PackageHash {
506 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
507 if serializer.is_human_readable() {
508 self.to_formatted_string().serialize(serializer)
509 } else {
510 self.0.serialize(serializer)
511 }
512 }
513}
514
515impl<'de> Deserialize<'de> for PackageHash {
516 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
517 if deserializer.is_human_readable() {
518 let formatted_string = String::deserialize(deserializer)?;
519 PackageHash::from_formatted_str(&formatted_string).map_err(SerdeError::custom)
520 } else {
521 let bytes = HashAddr::deserialize(deserializer)?;
522 Ok(PackageHash(bytes))
523 }
524 }
525}
526
527impl AsRef<[u8]> for PackageHash {
528 fn as_ref(&self) -> &[u8] {
529 self.0.as_ref()
530 }
531}
532
533impl TryFrom<&[u8]> for PackageHash {
534 type Error = TryFromSliceForPackageHashError;
535
536 fn try_from(bytes: &[u8]) -> Result<Self, TryFromSliceForPackageHashError> {
537 HashAddr::try_from(bytes)
538 .map(PackageHash::new)
539 .map_err(|_| TryFromSliceForPackageHashError(()))
540 }
541}
542
543impl TryFrom<&Vec<u8>> for PackageHash {
544 type Error = TryFromSliceForPackageHashError;
545
546 fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
547 HashAddr::try_from(bytes as &[u8])
548 .map(PackageHash::new)
549 .map_err(|_| TryFromSliceForPackageHashError(()))
550 }
551}
552
553impl From<&PublicKey> for PackageHash {
554 fn from(public_key: &PublicKey) -> Self {
555 PackageHash::from_public_key(public_key, crypto::blake2b)
556 }
557}
558
559#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
561#[cfg_attr(feature = "datasize", derive(DataSize))]
562#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
563pub enum PackageStatus {
564 Locked,
566 Unlocked,
568}
569
570impl PackageStatus {
571 pub fn new(is_locked: bool) -> Self {
573 if is_locked {
574 PackageStatus::Locked
575 } else {
576 PackageStatus::Unlocked
577 }
578 }
579}
580
581impl Default for PackageStatus {
582 fn default() -> Self {
583 Self::Unlocked
584 }
585}
586
587impl ToBytes for PackageStatus {
588 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
589 let mut result = bytesrepr::allocate_buffer(self)?;
590 match self {
591 PackageStatus::Unlocked => result.append(&mut false.to_bytes()?),
592 PackageStatus::Locked => result.append(&mut true.to_bytes()?),
593 }
594 Ok(result)
595 }
596
597 fn serialized_length(&self) -> usize {
598 match self {
599 PackageStatus::Unlocked => false.serialized_length(),
600 PackageStatus::Locked => true.serialized_length(),
601 }
602 }
603
604 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
605 match self {
606 PackageStatus::Locked => writer.push(u8::from(true)),
607 PackageStatus::Unlocked => writer.push(u8::from(false)),
608 }
609 Ok(())
610 }
611}
612
613impl FromBytes for PackageStatus {
614 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
615 let (val, bytes) = bool::from_bytes(bytes)?;
616 let status = PackageStatus::new(val);
617 Ok((status, bytes))
618 }
619}
620
621#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
623#[cfg_attr(feature = "datasize", derive(DataSize))]
624#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
625pub struct Package {
626 versions: EntityVersions,
628 disabled_versions: BTreeSet<EntityVersionKey>,
631 groups: Groups,
635 lock_status: PackageStatus,
637}
638
639impl CLTyped for Package {
640 fn cl_type() -> CLType {
641 CLType::Any
642 }
643}
644
645impl Package {
646 pub fn new(
648 versions: EntityVersions,
649 disabled_versions: BTreeSet<EntityVersionKey>,
650 groups: Groups,
651 lock_status: PackageStatus,
652 ) -> Self {
653 Package {
654 versions,
655 disabled_versions,
656 groups,
657 lock_status,
658 }
659 }
660
661 pub fn enable_version(&mut self, entity_addr: EntityAddr) -> Result<(), Error> {
663 let entity_version_key = self
664 .find_entity_version_key_by_hash(&entity_addr)
665 .copied()
666 .ok_or(Error::EntityNotFound)?;
667
668 self.disabled_versions.remove(&entity_version_key);
669
670 Ok(())
671 }
672
673 pub fn groups_mut(&mut self) -> &mut Groups {
675 &mut self.groups
676 }
677
678 pub fn groups(&self) -> &Groups {
680 &self.groups
681 }
682
683 pub fn add_group(&mut self, group: Group, urefs: BTreeSet<URef>) {
685 let v = self.groups.0.entry(group).or_default();
686 v.extend(urefs)
687 }
688
689 pub fn lookup_entity_hash(&self, entity_version_key: EntityVersionKey) -> Option<&EntityAddr> {
691 self.versions.0.get(&entity_version_key)
692 }
693
694 pub fn is_version_missing(&self, entity_version_key: EntityVersionKey) -> bool {
696 !self.versions.0.contains_key(&entity_version_key)
697 }
698
699 pub fn is_version_enabled(&self, entity_version_key: EntityVersionKey) -> bool {
701 !self.is_version_missing(entity_version_key)
702 && !self.disabled_versions.contains(&entity_version_key)
703 }
704
705 pub fn is_entity_enabled(&self, entity_hash: &EntityAddr) -> bool {
707 match self.find_entity_version_key_by_hash(entity_hash) {
708 Some(version_key) => !self.disabled_versions.contains(version_key),
709 None => false,
710 }
711 }
712
713 pub fn insert_entity_version(
715 &mut self,
716 protocol_version_major: ProtocolVersionMajor,
717 entity_hash: EntityAddr,
718 ) -> EntityVersionKey {
719 let contract_version = self.next_entity_version_for(protocol_version_major);
720 let key = EntityVersionKey::new(protocol_version_major, contract_version);
721 self.versions.0.insert(key, entity_hash);
722 key
723 }
724
725 pub fn disable_entity_version(&mut self, entity_hash: EntityAddr) -> Result<(), Error> {
727 let entity_version_key = self
728 .versions
729 .0
730 .iter()
731 .filter_map(|(k, v)| if *v == entity_hash { Some(*k) } else { None })
732 .next()
733 .ok_or(Error::EntityNotFound)?;
734
735 if !self.disabled_versions.contains(&entity_version_key) {
736 self.disabled_versions.insert(entity_version_key);
737 }
738
739 Ok(())
740 }
741
742 fn find_entity_version_key_by_hash(
743 &self,
744 entity_hash: &EntityAddr,
745 ) -> Option<&EntityVersionKey> {
746 self.versions
747 .0
748 .iter()
749 .filter_map(|(k, v)| if v == entity_hash { Some(k) } else { None })
750 .next()
751 }
752
753 pub fn versions(&self) -> &EntityVersions {
755 &self.versions
756 }
757
758 pub fn enabled_versions(&self) -> EntityVersions {
760 let mut ret = EntityVersions::new();
761 for version in &self.versions.0 {
762 if !self.is_version_enabled(*version.0) {
763 continue;
764 }
765 ret.0.insert(*version.0, *version.1);
766 }
767 ret
768 }
769
770 pub fn versions_mut(&mut self) -> &mut EntityVersions {
772 &mut self.versions
773 }
774
775 pub fn take_versions(self) -> EntityVersions {
777 self.versions
778 }
779
780 pub fn disabled_versions(&self) -> &BTreeSet<EntityVersionKey> {
782 &self.disabled_versions
783 }
784
785 pub fn disabled_versions_mut(&mut self) -> &mut BTreeSet<EntityVersionKey> {
787 &mut self.disabled_versions
788 }
789
790 pub fn remove_group(&mut self, group: &Group) -> bool {
792 self.groups.0.remove(group).is_some()
793 }
794
795 pub fn next_entity_version_for(&self, protocol_version: ProtocolVersionMajor) -> EntityVersion {
797 let current_version = self
798 .versions
799 .0
800 .keys()
801 .rev()
802 .find_map(|&entity_version_key| {
803 if entity_version_key.protocol_version_major() == protocol_version {
804 Some(entity_version_key.entity_version())
805 } else {
806 None
807 }
808 })
809 .unwrap_or(0);
810
811 current_version + 1
812 }
813
814 pub fn current_entity_version(&self) -> Option<EntityVersionKey> {
816 self.enabled_versions().0.keys().next_back().copied()
817 }
818
819 pub fn current_entity_hash(&self) -> Option<EntityAddr> {
821 self.enabled_versions().0.values().next_back().copied()
822 }
823
824 pub fn is_locked(&self) -> bool {
826 if self.versions.0.is_empty() {
827 return false;
828 }
829
830 match self.lock_status {
831 PackageStatus::Unlocked => false,
832 PackageStatus::Locked => true,
833 }
834 }
835
836 pub fn get_lock_status(&self) -> PackageStatus {
838 self.lock_status.clone()
839 }
840}
841
842impl ToBytes for Package {
843 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
844 let mut buffer = bytesrepr::allocate_buffer(self)?;
845 self.write_bytes(&mut buffer)?;
846 Ok(buffer)
847 }
848
849 fn serialized_length(&self) -> usize {
850 self.versions.serialized_length()
851 + self.disabled_versions.serialized_length()
852 + self.groups.serialized_length()
853 + self.lock_status.serialized_length()
854 }
855
856 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
857 self.versions().write_bytes(writer)?;
858 self.disabled_versions().write_bytes(writer)?;
859 self.groups().write_bytes(writer)?;
860 self.lock_status.write_bytes(writer)?;
861
862 Ok(())
863 }
864}
865
866impl FromBytes for Package {
867 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
868 let (versions, bytes) = EntityVersions::from_bytes(bytes)?;
869 let (disabled_versions, bytes) = BTreeSet::<EntityVersionKey>::from_bytes(bytes)?;
870 let (groups, bytes) = Groups::from_bytes(bytes)?;
871 let (lock_status, bytes) = PackageStatus::from_bytes(bytes)?;
872
873 let result = Package {
874 versions,
875 disabled_versions,
876 groups,
877 lock_status,
878 };
879
880 Ok((result, bytes))
881 }
882}
883
884#[cfg(test)]
885mod tests {
886 use core::iter::FromIterator;
887
888 use super::*;
889 use crate::{
890 AccessRights, EntityEntryPoint, EntityVersionKey, EntryPointAccess, EntryPointPayment,
891 EntryPointType, Parameter, ProtocolVersion, URef,
892 };
893 use alloc::borrow::ToOwned;
894
895 const ENTITY_HASH_V1: EntityAddr = EntityAddr::new_smart_contract([42; 32]);
896 const ENTITY_HASH_V2: EntityAddr = EntityAddr::new_smart_contract([84; 32]);
897
898 fn make_package_with_two_versions() -> Package {
899 let mut package = Package::new(
900 EntityVersions::default(),
901 BTreeSet::new(),
902 Groups::default(),
903 PackageStatus::default(),
904 );
905
906 {
908 let group_urefs = {
909 let mut ret = BTreeSet::new();
910 ret.insert(URef::new([1; 32], AccessRights::READ));
911 ret
912 };
913
914 package
915 .groups_mut()
916 .insert(Group::new("Group 1"), group_urefs.clone());
917
918 package
919 .groups_mut()
920 .insert(Group::new("Group 2"), group_urefs);
921 }
922
923 let _entry_points = {
925 let mut ret = BTreeMap::new();
926 let entrypoint = EntityEntryPoint::new(
927 "method0".to_string(),
928 vec![],
929 CLType::U32,
930 EntryPointAccess::groups(&["Group 2"]),
931 EntryPointType::Caller,
932 EntryPointPayment::Caller,
933 );
934 ret.insert(entrypoint.name().to_owned(), entrypoint);
935 let entrypoint = EntityEntryPoint::new(
936 "method1".to_string(),
937 vec![Parameter::new("Foo", CLType::U32)],
938 CLType::U32,
939 EntryPointAccess::groups(&["Group 1"]),
940 EntryPointType::Caller,
941 EntryPointPayment::Caller,
942 );
943 ret.insert(entrypoint.name().to_owned(), entrypoint);
944 ret
945 };
946
947 let protocol_version = ProtocolVersion::V1_0_0;
948
949 let v1 = package.insert_entity_version(protocol_version.value().major, ENTITY_HASH_V1);
950 let v2 = package.insert_entity_version(protocol_version.value().major, ENTITY_HASH_V2);
951 assert!(v2 > v1);
952
953 package
954 }
955
956 #[test]
957 fn next_entity_version() {
958 let major = 1;
959 let mut package = Package::new(
960 EntityVersions::default(),
961 BTreeSet::default(),
962 Groups::default(),
963 PackageStatus::default(),
964 );
965 assert_eq!(package.next_entity_version_for(major), 1);
966
967 let next_version =
968 package.insert_entity_version(major, EntityAddr::SmartContract([123; 32]));
969 assert_eq!(next_version, EntityVersionKey::new(major, 1));
970 assert_eq!(package.next_entity_version_for(major), 2);
971 let next_version_2 =
972 package.insert_entity_version(major, EntityAddr::SmartContract([124; 32]));
973 assert_eq!(next_version_2, EntityVersionKey::new(major, 2));
974
975 let major = 2;
976 assert_eq!(package.next_entity_version_for(major), 1);
977 let next_version_3 =
978 package.insert_entity_version(major, EntityAddr::SmartContract([42; 32]));
979 assert_eq!(next_version_3, EntityVersionKey::new(major, 1));
980 }
981
982 #[test]
983 fn roundtrip_serialization() {
984 let package = make_package_with_two_versions();
985 let bytes = package.to_bytes().expect("should serialize");
986 let (decoded_package, rem) = Package::from_bytes(&bytes).expect("should deserialize");
987 assert_eq!(package, decoded_package);
988 assert_eq!(rem.len(), 0);
989 }
990
991 #[test]
992 fn should_remove_group() {
993 let mut package = make_package_with_two_versions();
994
995 assert!(!package.remove_group(&Group::new("Non-existent group")));
996 assert!(package.remove_group(&Group::new("Group 1")));
997 assert!(!package.remove_group(&Group::new("Group 1"))); }
999
1000 #[test]
1001 fn should_disable_and_enable_entity_version() {
1002 const ENTITY_HASH: EntityAddr = EntityAddr::new_smart_contract([123; 32]);
1003
1004 let mut package = make_package_with_two_versions();
1005
1006 assert!(
1007 !package.is_entity_enabled(&ENTITY_HASH),
1008 "nonexisting entity should return false"
1009 );
1010
1011 assert_eq!(
1012 package.current_entity_version(),
1013 Some(EntityVersionKey::new(1, 2))
1014 );
1015 assert_eq!(package.current_entity_hash(), Some(ENTITY_HASH_V2));
1016
1017 assert_eq!(
1018 package.versions(),
1019 &EntityVersions::from(BTreeMap::from_iter([
1020 (EntityVersionKey::new(1, 1), ENTITY_HASH_V1),
1021 (EntityVersionKey::new(1, 2), ENTITY_HASH_V2)
1022 ])),
1023 );
1024 assert_eq!(
1025 package.enabled_versions(),
1026 EntityVersions::from(BTreeMap::from_iter([
1027 (EntityVersionKey::new(1, 1), ENTITY_HASH_V1),
1028 (EntityVersionKey::new(1, 2), ENTITY_HASH_V2)
1029 ])),
1030 );
1031
1032 assert!(!package.is_entity_enabled(&ENTITY_HASH));
1033
1034 assert_eq!(
1035 package.disable_entity_version(ENTITY_HASH),
1036 Err(Error::EntityNotFound),
1037 "should return entity not found error"
1038 );
1039
1040 assert!(
1041 !package.is_entity_enabled(&ENTITY_HASH),
1042 "disabling missing entity shouldnt change outcome"
1043 );
1044
1045 let next_version = package.insert_entity_version(1, ENTITY_HASH);
1046 assert!(
1047 package.is_version_enabled(next_version),
1048 "version should exist and be enabled"
1049 );
1050 assert!(package.is_entity_enabled(&ENTITY_HASH));
1051
1052 assert!(
1053 package.is_entity_enabled(&ENTITY_HASH),
1054 "entity should be enabled"
1055 );
1056
1057 assert_eq!(
1058 package.disable_entity_version(ENTITY_HASH),
1059 Ok(()),
1060 "should be able to disable version"
1061 );
1062 assert!(!package.is_entity_enabled(&ENTITY_HASH));
1063
1064 assert!(
1065 !package.is_entity_enabled(&ENTITY_HASH),
1066 "entity should be disabled"
1067 );
1068 assert!(
1075 !package.is_version_enabled(next_version),
1076 "version should not be enabled"
1077 );
1078
1079 assert_eq!(
1080 package.current_entity_version(),
1081 Some(EntityVersionKey::new(1, 2))
1082 );
1083 assert_eq!(package.current_entity_hash(), Some(ENTITY_HASH_V2));
1084 assert_eq!(
1085 package.versions(),
1086 &EntityVersions::from(BTreeMap::from_iter([
1087 (EntityVersionKey::new(1, 1), ENTITY_HASH_V1),
1088 (EntityVersionKey::new(1, 2), ENTITY_HASH_V2),
1089 (next_version, ENTITY_HASH),
1090 ])),
1091 );
1092 assert_eq!(
1093 package.enabled_versions(),
1094 EntityVersions::from(BTreeMap::from_iter([
1095 (EntityVersionKey::new(1, 1), ENTITY_HASH_V1),
1096 (EntityVersionKey::new(1, 2), ENTITY_HASH_V2),
1097 ])),
1098 );
1099 assert_eq!(
1100 package.disabled_versions(),
1101 &BTreeSet::from_iter([next_version]),
1102 );
1103
1104 assert_eq!(
1105 package.current_entity_version(),
1106 Some(EntityVersionKey::new(1, 2))
1107 );
1108 assert_eq!(package.current_entity_hash(), Some(ENTITY_HASH_V2));
1109
1110 assert_eq!(
1111 package.disable_entity_version(ENTITY_HASH_V2),
1112 Ok(()),
1113 "should be able to disable version 2"
1114 );
1115
1116 assert_eq!(
1117 package.enabled_versions(),
1118 EntityVersions::from(BTreeMap::from_iter([(
1119 EntityVersionKey::new(1, 1),
1120 ENTITY_HASH_V1
1121 ),])),
1122 );
1123
1124 assert_eq!(
1125 package.current_entity_version(),
1126 Some(EntityVersionKey::new(1, 1))
1127 );
1128 assert_eq!(package.current_entity_hash(), Some(ENTITY_HASH_V1));
1129
1130 assert_eq!(
1131 package.disabled_versions(),
1132 &BTreeSet::from_iter([next_version, EntityVersionKey::new(1, 2)]),
1133 );
1134
1135 assert_eq!(package.enable_version(ENTITY_HASH_V2), Ok(()),);
1136
1137 assert_eq!(
1138 package.enabled_versions(),
1139 EntityVersions::from(BTreeMap::from_iter([
1140 (EntityVersionKey::new(1, 1), ENTITY_HASH_V1),
1141 (EntityVersionKey::new(1, 2), ENTITY_HASH_V2),
1142 ])),
1143 );
1144
1145 assert_eq!(
1146 package.disabled_versions(),
1147 &BTreeSet::from_iter([next_version])
1148 );
1149
1150 assert_eq!(package.current_entity_hash(), Some(ENTITY_HASH_V2));
1151
1152 assert_eq!(package.enable_version(ENTITY_HASH), Ok(()),);
1153
1154 assert_eq!(
1155 package.enable_version(ENTITY_HASH),
1156 Ok(()),
1157 "enabling a entity twice should be a noop"
1158 );
1159
1160 assert_eq!(
1161 package.enabled_versions(),
1162 EntityVersions::from(BTreeMap::from_iter([
1163 (EntityVersionKey::new(1, 1), ENTITY_HASH_V1),
1164 (EntityVersionKey::new(1, 2), ENTITY_HASH_V2),
1165 (next_version, ENTITY_HASH),
1166 ])),
1167 );
1168
1169 assert_eq!(package.disabled_versions(), &BTreeSet::new(),);
1170
1171 assert_eq!(package.current_entity_hash(), Some(ENTITY_HASH));
1172 }
1173
1174 #[test]
1175 fn should_not_allow_to_enable_non_existing_version() {
1176 let mut package = make_package_with_two_versions();
1177
1178 assert_eq!(
1179 package.enable_version(EntityAddr::SmartContract(HashAddr::default())),
1180 Err(Error::EntityNotFound),
1181 );
1182 }
1183
1184 #[test]
1185 fn package_hash_from_slice() {
1186 let bytes: Vec<u8> = (0..32).collect();
1187 let package_hash = HashAddr::try_from(&bytes[..]).expect("should create package hash");
1188 let package_hash = PackageHash::new(package_hash);
1189 assert_eq!(&bytes, &package_hash.as_bytes());
1190 }
1191
1192 #[test]
1193 fn package_hash_from_str() {
1194 let package_hash = PackageHash::new([3; 32]);
1195 let encoded = package_hash.to_formatted_string();
1196 let decoded = PackageHash::from_formatted_str(&encoded).unwrap();
1197 assert_eq!(package_hash, decoded);
1198
1199 let invalid_prefix =
1200 "package0000000000000000000000000000000000000000000000000000000000000000";
1201 assert!(matches!(
1202 PackageHash::from_formatted_str(invalid_prefix).unwrap_err(),
1203 FromStrError::InvalidPrefix
1204 ));
1205
1206 let short_addr = "package-00000000000000000000000000000000000000000000000000000000000000";
1207 assert!(matches!(
1208 PackageHash::from_formatted_str(short_addr).unwrap_err(),
1209 FromStrError::Hash(_)
1210 ));
1211
1212 let long_addr =
1213 "package-000000000000000000000000000000000000000000000000000000000000000000";
1214 assert!(matches!(
1215 PackageHash::from_formatted_str(long_addr).unwrap_err(),
1216 FromStrError::Hash(_)
1217 ));
1218
1219 let invalid_hex =
1220 "package-000000000000000000000000000000000000000000000000000000000000000g";
1221 assert!(matches!(
1222 PackageHash::from_formatted_str(invalid_hex).unwrap_err(),
1223 FromStrError::Hex(_)
1224 ));
1225 }
1226}
1227
1228#[cfg(test)]
1229mod prop_tests {
1230 use proptest::prelude::*;
1231
1232 use crate::{bytesrepr, gens};
1233
1234 proptest! {
1235 #[test]
1236 fn test_value_contract_package(contract_pkg in gens::package_arb()) {
1237 bytesrepr::test_serialization_roundtrip(&contract_pkg);
1238 }
1239 }
1240}