casper_types/
package.rs

1//! Module containing the Package and associated types for addressable entities.
2
3use 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/// Associated error type of `TryFrom<&[u8]>` for `ContractHash`.
38#[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/// A (labelled) "user group". Each method of a versioned contract may be
48/// associated with one or more user groups which are allowed to call it.
49#[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    /// Basic constructor
56    pub fn new<T: Into<String>>(s: T) -> Self {
57        Group(s.into())
58    }
59
60    /// Retrieves underlying name.
61    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
93/// Automatically incremented value for a contract version within a major `ProtocolVersion`.
94pub type EntityVersion = u32;
95
96/// Within each discrete major `ProtocolVersion`, entity version resets to this value.
97pub const ENTITY_INITIAL_VERSION: EntityVersion = 1;
98
99/// Major element of `ProtocolVersion` a `EntityVersion` is compatible with.
100pub type ProtocolVersionMajor = u32;
101
102/// Major element of `ProtocolVersion` combined with `EntityVersion`.
103#[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    /// Major element of `ProtocolVersion` a `ContractVersion` is compatible with.
108    protocol_version_major: ProtocolVersionMajor,
109    /// Automatically incremented value for a contract version within a major `ProtocolVersion`.
110    entity_version: EntityVersion,
111}
112
113impl EntityVersionKey {
114    /// Returns a new instance of ContractVersionKey with provided values.
115    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    /// Returns the major element of the protocol version this contract is compatible with.
126    pub fn protocol_version_major(self) -> ProtocolVersionMajor {
127        self.protocol_version_major
128    }
129
130    /// Returns the contract version within the protocol major version.
131    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
192/// Serialized length of `EntityVersionKey`.
193pub const ENTITY_VERSION_KEY_SERIALIZED_LENGTH: usize =
194    U32_SERIALIZED_LENGTH + U32_SERIALIZED_LENGTH;
195
196/// Collection of entity versions.
197#[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    /// Constructs a new, empty `EntityVersions`.
208    pub const fn new() -> Self {
209        EntityVersions(BTreeMap::new())
210    }
211
212    /// Returns an iterator over the `AddressableEntityHash`s (i.e. the map's values).
213    pub fn contract_hashes(&self) -> impl Iterator<Item = &EntityAddr> {
214        self.0.values()
215    }
216
217    /// Returns the `AddressableEntityHash` under the key
218    pub fn get(&self, key: &EntityVersionKey) -> Option<&EntityAddr> {
219        self.0.get(key)
220    }
221
222    /// Retrieve the first entity version key if it exists
223    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    /// The number of versions present in the package.
232    pub fn version_count(&self) -> usize {
233        self.0.len()
234    }
235
236    /// Returns the latest entity version key if it exists.
237    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/// Collection of named groups.
283#[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    /// Constructs a new, empty `Groups`.
294    pub const fn new() -> Self {
295        Groups(BTreeMap::new())
296    }
297
298    /// Inserts a named group.
299    ///
300    /// If the map did not have this name present, `None` is returned.  If the map did have this
301    /// name present, its collection of `URef`s is overwritten, and the collection is returned.
302    pub fn insert(&mut self, name: Group, urefs: BTreeSet<URef>) -> Option<BTreeSet<URef>> {
303        self.0.insert(name, urefs)
304    }
305
306    /// Returns `true` if the named group exists in the collection.
307    pub fn contains(&self, name: &Group) -> bool {
308        self.0.contains_key(name)
309    }
310
311    /// Returns a reference to the collection of `URef`s under the given `name` if any.
312    pub fn get(&self, name: &Group) -> Option<&BTreeSet<URef>> {
313        self.0.get(name)
314    }
315
316    /// Returns a mutable reference to the collection of `URef`s under the given `name` if any.
317    pub fn get_mut(&mut self, name: &Group) -> Option<&mut BTreeSet<URef>> {
318        self.0.get_mut(name)
319    }
320
321    /// Returns the number of named groups.
322    pub fn len(&self) -> usize {
323        self.0.len()
324    }
325
326    /// Returns `true` if there are no named groups.
327    pub fn is_empty(&self) -> bool {
328        self.0.is_empty()
329    }
330
331    /// Returns an iterator over the `Key`s (i.e. the map's values).
332    pub fn keys(&self) -> impl Iterator<Item = &BTreeSet<URef>> {
333        self.0.values()
334    }
335
336    /// Returns the total number of `URef`s contained in all the groups.
337    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/// A newtype wrapping a `HashAddr` which references a [`Package`] in the global state.
383#[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    /// Constructs a new `PackageHash` from the raw bytes of the package hash.
396    pub const fn new(value: HashAddr) -> PackageHash {
397        PackageHash(value)
398    }
399
400    /// Returns the raw bytes of the entity hash as an array.
401    pub fn value(&self) -> HashAddr {
402        self.0
403    }
404
405    /// Returns the raw bytes of the entity hash as a `slice`.
406    pub fn as_bytes(&self) -> &[u8] {
407        &self.0
408    }
409
410    /// Formats the `PackageHash` for users getting and putting.
411    pub fn to_formatted_string(self) -> String {
412        format!("{}{}", PACKAGE_STRING_PREFIX, base16::encode_lower(&self.0),)
413    }
414
415    /// Parses a string formatted as per `Self::to_formatted_string()` into a
416    /// `PackageHash`.
417    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    /// Parses a `PublicKey` and outputs the corresponding account hash.
427    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        // Prepare preimage based on the public key parameters.
443        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        // Hash the preimage data using blake2b256 and return it.
451        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/// A enum to determine the lock status of the package.
560#[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    /// The package is locked and cannot be versioned.
565    Locked,
566    /// The package is unlocked and can be versioned.
567    Unlocked,
568}
569
570impl PackageStatus {
571    /// Create a new status flag based on a boolean value
572    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/// Entity definition, metadata, and security container.
622#[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    /// All versions (enabled & disabled).
627    versions: EntityVersions,
628    /// Collection of disabled entity versions. The runtime will not permit disabled entity
629    /// versions to be executed.
630    disabled_versions: BTreeSet<EntityVersionKey>,
631    /// Mapping maintaining the set of URefs associated with each "user group". This can be used to
632    /// control access to methods in a particular version of the entity. A method is callable by
633    /// any context which "knows" any of the URefs associated with the method's user group.
634    groups: Groups,
635    /// A flag that determines whether a entity is locked
636    lock_status: PackageStatus,
637}
638
639impl CLTyped for Package {
640    fn cl_type() -> CLType {
641        CLType::Any
642    }
643}
644
645impl Package {
646    /// Create new `Package` (with no versions) from given access key.
647    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    /// Enable the entity version corresponding to the given hash (if it exists).
662    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    /// Get the mutable group definitions for this entity.
674    pub fn groups_mut(&mut self) -> &mut Groups {
675        &mut self.groups
676    }
677
678    /// Get the group definitions for this entity.
679    pub fn groups(&self) -> &Groups {
680        &self.groups
681    }
682
683    /// Adds new group to this entity.
684    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    /// Lookup the entity hash for a given entity version (if present)
690    pub fn lookup_entity_hash(&self, entity_version_key: EntityVersionKey) -> Option<&EntityAddr> {
691        self.versions.0.get(&entity_version_key)
692    }
693
694    /// Checks if the given entity version exists.
695    pub fn is_version_missing(&self, entity_version_key: EntityVersionKey) -> bool {
696        !self.versions.0.contains_key(&entity_version_key)
697    }
698
699    /// Checks if the given entity version exists and is available for use.
700    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    /// Returns `true` if the given entity hash exists and is enabled.
706    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    /// Insert a new entity version; the next sequential version number will be issued.
714    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    /// Disable the entity version corresponding to the given hash (if it exists).
726    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    /// Returns reference to all of this entity's versions.
754    pub fn versions(&self) -> &EntityVersions {
755        &self.versions
756    }
757
758    /// Returns all of this entity's enabled entity versions.
759    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    /// Returns mutable reference to all of this entity's versions (enabled and disabled).
771    pub fn versions_mut(&mut self) -> &mut EntityVersions {
772        &mut self.versions
773    }
774
775    /// Consumes the object and returns all of this entity's versions (enabled and disabled).
776    pub fn take_versions(self) -> EntityVersions {
777        self.versions
778    }
779
780    /// Returns all of this entity's disabled versions.
781    pub fn disabled_versions(&self) -> &BTreeSet<EntityVersionKey> {
782        &self.disabled_versions
783    }
784
785    /// Returns mut reference to all of this entity's disabled versions.
786    pub fn disabled_versions_mut(&mut self) -> &mut BTreeSet<EntityVersionKey> {
787        &mut self.disabled_versions
788    }
789
790    /// Removes a group from this entity (if it exists).
791    pub fn remove_group(&mut self, group: &Group) -> bool {
792        self.groups.0.remove(group).is_some()
793    }
794
795    /// Gets the next available entity version for the given protocol version
796    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    /// Return the entity version key for the newest enabled entity version.
815    pub fn current_entity_version(&self) -> Option<EntityVersionKey> {
816        self.enabled_versions().0.keys().next_back().copied()
817    }
818
819    /// Return the entity hash for the newest enabled entity version.
820    pub fn current_entity_hash(&self) -> Option<EntityAddr> {
821        self.enabled_versions().0.values().next_back().copied()
822    }
823
824    /// Return the lock status of the entity package.
825    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    /// Return the package status itself
837    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        // add groups
907        {
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        // add entry_points
924        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"))); // Group no longer exists
998    }
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        // This was once true, but look up vs disable checking have been decoupled in 2.0
1069        // assert_eq!(
1070        //     package.lookup_entity_hash(next_version),
1071        //     None,
1072        //     "should not return disabled entity version"
1073        // );
1074        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}