casper_types/
contracts.rs

1//! Data types for supporting contract headers feature.
2// TODO - remove once schemars stops causing warning.
3#![allow(clippy::field_reassign_with_default)]
4
5use alloc::{
6    collections::{BTreeMap, BTreeSet},
7    format,
8    string::{String, ToString},
9    vec::Vec,
10};
11use core::{
12    array::TryFromSliceError,
13    convert::TryFrom,
14    fmt::{self, Debug, Display, Formatter},
15};
16
17#[cfg(feature = "datasize")]
18use datasize::DataSize;
19#[cfg(feature = "json-schema")]
20use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
21use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
22
23use crate::{
24    account,
25    account::TryFromSliceForAccountHashError,
26    bytesrepr::{self, FromBytes, ToBytes, U32_SERIALIZED_LENGTH},
27    checksummed_hex,
28    contract_wasm::ContractWasmHash,
29    uref,
30    uref::URef,
31    CLType, CLTyped, ContextAccessRights, HashAddr, Key, ProtocolVersion, KEY_HASH_LENGTH,
32};
33
34/// Maximum number of distinct user groups.
35pub const MAX_GROUPS: u8 = 10;
36/// Maximum number of URefs which can be assigned across all user groups.
37pub const MAX_TOTAL_UREFS: usize = 100;
38
39const CONTRACT_STRING_PREFIX: &str = "contract-";
40const PACKAGE_STRING_PREFIX: &str = "contract-package-";
41// We need to support the legacy prefix of "contract-package-wasm".
42const PACKAGE_STRING_LEGACY_EXTRA_PREFIX: &str = "wasm";
43
44/// Set of errors which may happen when working with contract headers.
45#[derive(Debug, PartialEq, Eq)]
46#[repr(u8)]
47#[non_exhaustive]
48pub enum Error {
49    /// Attempt to override an existing or previously existing version with a
50    /// new header (this is not allowed to ensure immutability of a given
51    /// version).
52    /// ```
53    /// # use casper_types::contracts::Error;
54    /// assert_eq!(1, Error::PreviouslyUsedVersion as u8);
55    /// ```
56    PreviouslyUsedVersion = 1,
57    /// Attempted to disable a contract that does not exist.
58    /// ```
59    /// # use casper_types::contracts::Error;
60    /// assert_eq!(2, Error::ContractNotFound as u8);
61    /// ```
62    ContractNotFound = 2,
63    /// Attempted to create a user group which already exists (use the update
64    /// function to change an existing user group).
65    /// ```
66    /// # use casper_types::contracts::Error;
67    /// assert_eq!(3, Error::GroupAlreadyExists as u8);
68    /// ```
69    GroupAlreadyExists = 3,
70    /// Attempted to add a new user group which exceeds the allowed maximum
71    /// number of groups.
72    /// ```
73    /// # use casper_types::contracts::Error;
74    /// assert_eq!(4, Error::MaxGroupsExceeded as u8);
75    /// ```
76    MaxGroupsExceeded = 4,
77    /// Attempted to add a new URef to a group, which resulted in the total
78    /// number of URefs across all user groups to exceed the allowed maximum.
79    /// ```
80    /// # use casper_types::contracts::Error;
81    /// assert_eq!(5, Error::MaxTotalURefsExceeded as u8);
82    /// ```
83    MaxTotalURefsExceeded = 5,
84    /// Attempted to remove a URef from a group, which does not exist in the
85    /// group.
86    /// ```
87    /// # use casper_types::contracts::Error;
88    /// assert_eq!(6, Error::GroupDoesNotExist as u8);
89    /// ```
90    GroupDoesNotExist = 6,
91    /// Attempted to remove unknown URef from the group.
92    /// ```
93    /// # use casper_types::contracts::Error;
94    /// assert_eq!(7, Error::UnableToRemoveURef as u8);
95    /// ```
96    UnableToRemoveURef = 7,
97    /// Group is use by at least one active contract.
98    /// ```
99    /// # use casper_types::contracts::Error;
100    /// assert_eq!(8, Error::GroupInUse as u8);
101    /// ```
102    GroupInUse = 8,
103    /// URef already exists in given group.
104    /// ```
105    /// # use casper_types::contracts::Error;
106    /// assert_eq!(9, Error::URefAlreadyExists as u8);
107    /// ```
108    URefAlreadyExists = 9,
109}
110
111impl TryFrom<u8> for Error {
112    type Error = ();
113
114    fn try_from(value: u8) -> Result<Self, Self::Error> {
115        let error = match value {
116            v if v == Self::PreviouslyUsedVersion as u8 => Self::PreviouslyUsedVersion,
117            v if v == Self::ContractNotFound as u8 => Self::ContractNotFound,
118            v if v == Self::GroupAlreadyExists as u8 => Self::GroupAlreadyExists,
119            v if v == Self::MaxGroupsExceeded as u8 => Self::MaxGroupsExceeded,
120            v if v == Self::MaxTotalURefsExceeded as u8 => Self::MaxTotalURefsExceeded,
121            v if v == Self::GroupDoesNotExist as u8 => Self::GroupDoesNotExist,
122            v if v == Self::UnableToRemoveURef as u8 => Self::UnableToRemoveURef,
123            v if v == Self::GroupInUse as u8 => Self::GroupInUse,
124            v if v == Self::URefAlreadyExists as u8 => Self::URefAlreadyExists,
125            _ => return Err(()),
126        };
127        Ok(error)
128    }
129}
130
131/// Associated error type of `TryFrom<&[u8]>` for `ContractHash`.
132#[derive(Debug)]
133pub struct TryFromSliceForContractHashError(());
134
135impl Display for TryFromSliceForContractHashError {
136    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
137        write!(f, "failed to retrieve from slice")
138    }
139}
140
141/// An error from parsing a formatted contract string
142#[derive(Debug)]
143#[non_exhaustive]
144pub enum FromStrError {
145    /// Invalid formatted string prefix.
146    InvalidPrefix,
147    /// Error when decoding a hex string
148    Hex(base16::DecodeError),
149    /// Error when parsing an account
150    Account(TryFromSliceForAccountHashError),
151    /// Error when parsing the hash.
152    Hash(TryFromSliceError),
153    /// Error when parsing an account hash.
154    AccountHash(account::FromStrError),
155    /// Error when parsing an uref.
156    URef(uref::FromStrError),
157}
158
159impl From<base16::DecodeError> for FromStrError {
160    fn from(error: base16::DecodeError) -> Self {
161        FromStrError::Hex(error)
162    }
163}
164
165impl From<TryFromSliceForAccountHashError> for FromStrError {
166    fn from(error: TryFromSliceForAccountHashError) -> Self {
167        FromStrError::Account(error)
168    }
169}
170
171impl From<TryFromSliceError> for FromStrError {
172    fn from(error: TryFromSliceError) -> Self {
173        FromStrError::Hash(error)
174    }
175}
176
177impl From<account::FromStrError> for FromStrError {
178    fn from(error: account::FromStrError) -> Self {
179        FromStrError::AccountHash(error)
180    }
181}
182
183impl From<uref::FromStrError> for FromStrError {
184    fn from(error: uref::FromStrError) -> Self {
185        FromStrError::URef(error)
186    }
187}
188
189impl Display for FromStrError {
190    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
191        match self {
192            FromStrError::InvalidPrefix => write!(f, "invalid prefix"),
193            FromStrError::Hex(error) => write!(f, "decode from hex: {}", error),
194            FromStrError::Account(error) => write!(f, "account from string error: {:?}", error),
195            FromStrError::Hash(error) => write!(f, "hash from string error: {}", error),
196            FromStrError::AccountHash(error) => {
197                write!(f, "account hash from string error: {:?}", error)
198            }
199            FromStrError::URef(error) => write!(f, "uref from string error: {:?}", error),
200        }
201    }
202}
203
204/// A (labelled) "user group". Each method of a versioned contract may be
205/// associated with one or more user groups which are allowed to call it.
206#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
207#[cfg_attr(feature = "datasize", derive(DataSize))]
208#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
209pub struct Group(String);
210
211impl Group {
212    /// Basic constructor
213    pub fn new<T: Into<String>>(s: T) -> Self {
214        Group(s.into())
215    }
216
217    /// Retrieves underlying name.
218    pub fn value(&self) -> &str {
219        &self.0
220    }
221}
222
223impl From<Group> for String {
224    fn from(group: Group) -> Self {
225        group.0
226    }
227}
228
229impl ToBytes for Group {
230    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
231        self.0.to_bytes()
232    }
233
234    fn serialized_length(&self) -> usize {
235        self.0.serialized_length()
236    }
237
238    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
239        self.value().write_bytes(writer)?;
240        Ok(())
241    }
242}
243
244impl FromBytes for Group {
245    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
246        String::from_bytes(bytes).map(|(label, bytes)| (Group(label), bytes))
247    }
248}
249
250/// Automatically incremented value for a contract version within a major `ProtocolVersion`.
251pub type ContractVersion = u32;
252
253/// Within each discrete major `ProtocolVersion`, contract version resets to this value.
254pub const CONTRACT_INITIAL_VERSION: ContractVersion = 1;
255
256/// Major element of `ProtocolVersion` a `ContractVersion` is compatible with.
257pub type ProtocolVersionMajor = u32;
258
259/// Major element of `ProtocolVersion` combined with `ContractVersion`.
260#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
261#[cfg_attr(feature = "datasize", derive(DataSize))]
262pub struct ContractVersionKey(ProtocolVersionMajor, ContractVersion);
263
264impl ContractVersionKey {
265    /// Returns a new instance of ContractVersionKey with provided values.
266    pub fn new(
267        protocol_version_major: ProtocolVersionMajor,
268        contract_version: ContractVersion,
269    ) -> Self {
270        Self(protocol_version_major, contract_version)
271    }
272
273    /// Returns the major element of the protocol version this contract is compatible with.
274    pub fn protocol_version_major(self) -> ProtocolVersionMajor {
275        self.0
276    }
277
278    /// Returns the contract version within the protocol major version.
279    pub fn contract_version(self) -> ContractVersion {
280        self.1
281    }
282}
283
284impl From<ContractVersionKey> for (ProtocolVersionMajor, ContractVersion) {
285    fn from(contract_version_key: ContractVersionKey) -> Self {
286        (contract_version_key.0, contract_version_key.1)
287    }
288}
289
290/// Serialized length of `ContractVersionKey`.
291pub const CONTRACT_VERSION_KEY_SERIALIZED_LENGTH: usize =
292    U32_SERIALIZED_LENGTH + U32_SERIALIZED_LENGTH;
293
294impl ToBytes for ContractVersionKey {
295    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
296        let mut ret = bytesrepr::unchecked_allocate_buffer(self);
297        ret.append(&mut self.0.to_bytes()?);
298        ret.append(&mut self.1.to_bytes()?);
299        Ok(ret)
300    }
301
302    fn serialized_length(&self) -> usize {
303        CONTRACT_VERSION_KEY_SERIALIZED_LENGTH
304    }
305
306    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
307        self.0.write_bytes(writer)?;
308        self.1.write_bytes(writer)?;
309        Ok(())
310    }
311}
312
313impl FromBytes for ContractVersionKey {
314    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
315        let (major, rem): (u32, &[u8]) = FromBytes::from_bytes(bytes)?;
316        let (contract, rem): (ContractVersion, &[u8]) = FromBytes::from_bytes(rem)?;
317        Ok((ContractVersionKey::new(major, contract), rem))
318    }
319}
320
321impl fmt::Display for ContractVersionKey {
322    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
323        write!(f, "{}.{}", self.0, self.1)
324    }
325}
326
327/// Collection of contract versions.
328pub type ContractVersions = BTreeMap<ContractVersionKey, ContractHash>;
329
330/// Collection of disabled contract versions. The runtime will not permit disabled
331/// contract versions to be executed.
332pub type DisabledVersions = BTreeSet<ContractVersionKey>;
333
334/// Collection of named groups.
335pub type Groups = BTreeMap<Group, BTreeSet<URef>>;
336
337/// A newtype wrapping a `HashAddr` which references a [`Contract`] in the global state.
338#[derive(Default, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
339#[cfg_attr(feature = "datasize", derive(DataSize))]
340pub struct ContractHash(HashAddr);
341
342impl ContractHash {
343    /// Constructs a new `ContractHash` from the raw bytes of the contract hash.
344    pub const fn new(value: HashAddr) -> ContractHash {
345        ContractHash(value)
346    }
347
348    /// Returns the raw bytes of the contract hash as an array.
349    pub fn value(&self) -> HashAddr {
350        self.0
351    }
352
353    /// Returns the raw bytes of the contract hash as a `slice`.
354    pub fn as_bytes(&self) -> &[u8] {
355        &self.0
356    }
357
358    /// Formats the `ContractHash` for users getting and putting.
359    pub fn to_formatted_string(self) -> String {
360        format!(
361            "{}{}",
362            CONTRACT_STRING_PREFIX,
363            base16::encode_lower(&self.0),
364        )
365    }
366
367    /// Parses a string formatted as per `Self::to_formatted_string()` into a
368    /// `ContractHash`.
369    pub fn from_formatted_str(input: &str) -> Result<Self, FromStrError> {
370        let remainder = input
371            .strip_prefix(CONTRACT_STRING_PREFIX)
372            .ok_or(FromStrError::InvalidPrefix)?;
373        let bytes = HashAddr::try_from(checksummed_hex::decode(remainder)?.as_ref())?;
374        Ok(ContractHash(bytes))
375    }
376}
377
378impl Display for ContractHash {
379    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
380        write!(f, "{}", base16::encode_lower(&self.0))
381    }
382}
383
384impl Debug for ContractHash {
385    fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
386        write!(f, "ContractHash({})", base16::encode_lower(&self.0))
387    }
388}
389
390impl CLTyped for ContractHash {
391    fn cl_type() -> CLType {
392        CLType::ByteArray(KEY_HASH_LENGTH as u32)
393    }
394}
395
396impl ToBytes for ContractHash {
397    #[inline(always)]
398    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
399        self.0.to_bytes()
400    }
401
402    #[inline(always)]
403    fn serialized_length(&self) -> usize {
404        self.0.serialized_length()
405    }
406
407    #[inline(always)]
408    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
409        writer.extend_from_slice(&self.0);
410        Ok(())
411    }
412}
413
414impl FromBytes for ContractHash {
415    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
416        let (bytes, rem) = FromBytes::from_bytes(bytes)?;
417        Ok((ContractHash::new(bytes), rem))
418    }
419}
420
421impl From<[u8; 32]> for ContractHash {
422    fn from(bytes: [u8; 32]) -> Self {
423        ContractHash(bytes)
424    }
425}
426
427impl Serialize for ContractHash {
428    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
429        if serializer.is_human_readable() {
430            self.to_formatted_string().serialize(serializer)
431        } else {
432            self.0.serialize(serializer)
433        }
434    }
435}
436
437impl<'de> Deserialize<'de> for ContractHash {
438    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
439        if deserializer.is_human_readable() {
440            let formatted_string = String::deserialize(deserializer)?;
441            ContractHash::from_formatted_str(&formatted_string).map_err(SerdeError::custom)
442        } else {
443            let bytes = HashAddr::deserialize(deserializer)?;
444            Ok(ContractHash(bytes))
445        }
446    }
447}
448
449impl AsRef<[u8]> for ContractHash {
450    fn as_ref(&self) -> &[u8] {
451        self.0.as_ref()
452    }
453}
454
455impl TryFrom<&[u8]> for ContractHash {
456    type Error = TryFromSliceForContractHashError;
457
458    fn try_from(bytes: &[u8]) -> Result<Self, TryFromSliceForContractHashError> {
459        HashAddr::try_from(bytes)
460            .map(ContractHash::new)
461            .map_err(|_| TryFromSliceForContractHashError(()))
462    }
463}
464
465impl TryFrom<&Vec<u8>> for ContractHash {
466    type Error = TryFromSliceForContractHashError;
467
468    fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
469        HashAddr::try_from(bytes as &[u8])
470            .map(ContractHash::new)
471            .map_err(|_| TryFromSliceForContractHashError(()))
472    }
473}
474
475#[cfg(feature = "json-schema")]
476impl JsonSchema for ContractHash {
477    fn schema_name() -> String {
478        String::from("ContractHash")
479    }
480
481    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
482        let schema = gen.subschema_for::<String>();
483        let mut schema_object = schema.into_object();
484        schema_object.metadata().description = Some("The hash address of the contract".to_string());
485        schema_object.into()
486    }
487}
488
489/// A newtype wrapping a `HashAddr` which references a [`ContractPackage`] in the global state.
490#[derive(Default, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
491#[cfg_attr(feature = "datasize", derive(DataSize))]
492pub struct ContractPackageHash(HashAddr);
493
494impl ContractPackageHash {
495    /// Constructs a new `ContractPackageHash` from the raw bytes of the contract package hash.
496    pub const fn new(value: HashAddr) -> ContractPackageHash {
497        ContractPackageHash(value)
498    }
499
500    /// Returns the raw bytes of the contract hash as an array.
501    pub fn value(&self) -> HashAddr {
502        self.0
503    }
504
505    /// Returns the raw bytes of the contract hash as a `slice`.
506    pub fn as_bytes(&self) -> &[u8] {
507        &self.0
508    }
509
510    /// Formats the `ContractPackageHash` for users getting and putting.
511    pub fn to_formatted_string(self) -> String {
512        format!("{}{}", PACKAGE_STRING_PREFIX, base16::encode_lower(&self.0),)
513    }
514
515    /// Parses a string formatted as per `Self::to_formatted_string()` into a
516    /// `ContractPackageHash`.
517    pub fn from_formatted_str(input: &str) -> Result<Self, FromStrError> {
518        let remainder = input
519            .strip_prefix(PACKAGE_STRING_PREFIX)
520            .ok_or(FromStrError::InvalidPrefix)?;
521
522        let hex_addr = remainder
523            .strip_prefix(PACKAGE_STRING_LEGACY_EXTRA_PREFIX)
524            .unwrap_or(remainder);
525
526        let bytes = HashAddr::try_from(checksummed_hex::decode(hex_addr)?.as_ref())?;
527        Ok(ContractPackageHash(bytes))
528    }
529}
530
531impl Display for ContractPackageHash {
532    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
533        write!(f, "{}", base16::encode_lower(&self.0))
534    }
535}
536
537impl Debug for ContractPackageHash {
538    fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
539        write!(f, "ContractPackageHash({})", base16::encode_lower(&self.0))
540    }
541}
542
543impl CLTyped for ContractPackageHash {
544    fn cl_type() -> CLType {
545        CLType::ByteArray(KEY_HASH_LENGTH as u32)
546    }
547}
548
549impl ToBytes for ContractPackageHash {
550    #[inline(always)]
551    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
552        self.0.to_bytes()
553    }
554
555    #[inline(always)]
556    fn serialized_length(&self) -> usize {
557        self.0.serialized_length()
558    }
559
560    #[inline(always)]
561    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
562        writer.extend_from_slice(&self.0);
563        Ok(())
564    }
565}
566
567impl FromBytes for ContractPackageHash {
568    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
569        let (bytes, rem) = FromBytes::from_bytes(bytes)?;
570        Ok((ContractPackageHash::new(bytes), rem))
571    }
572}
573
574impl From<[u8; 32]> for ContractPackageHash {
575    fn from(bytes: [u8; 32]) -> Self {
576        ContractPackageHash(bytes)
577    }
578}
579
580impl Serialize for ContractPackageHash {
581    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
582        if serializer.is_human_readable() {
583            self.to_formatted_string().serialize(serializer)
584        } else {
585            self.0.serialize(serializer)
586        }
587    }
588}
589
590impl<'de> Deserialize<'de> for ContractPackageHash {
591    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
592        if deserializer.is_human_readable() {
593            let formatted_string = String::deserialize(deserializer)?;
594            ContractPackageHash::from_formatted_str(&formatted_string).map_err(SerdeError::custom)
595        } else {
596            let bytes = HashAddr::deserialize(deserializer)?;
597            Ok(ContractPackageHash(bytes))
598        }
599    }
600}
601
602impl AsRef<[u8]> for ContractPackageHash {
603    fn as_ref(&self) -> &[u8] {
604        self.0.as_ref()
605    }
606}
607
608impl TryFrom<&[u8]> for ContractPackageHash {
609    type Error = TryFromSliceForContractHashError;
610
611    fn try_from(bytes: &[u8]) -> Result<Self, TryFromSliceForContractHashError> {
612        HashAddr::try_from(bytes)
613            .map(ContractPackageHash::new)
614            .map_err(|_| TryFromSliceForContractHashError(()))
615    }
616}
617
618impl TryFrom<&Vec<u8>> for ContractPackageHash {
619    type Error = TryFromSliceForContractHashError;
620
621    fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
622        HashAddr::try_from(bytes as &[u8])
623            .map(ContractPackageHash::new)
624            .map_err(|_| TryFromSliceForContractHashError(()))
625    }
626}
627
628#[cfg(feature = "json-schema")]
629impl JsonSchema for ContractPackageHash {
630    fn schema_name() -> String {
631        String::from("ContractPackageHash")
632    }
633
634    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
635        let schema = gen.subschema_for::<String>();
636        let mut schema_object = schema.into_object();
637        schema_object.metadata().description =
638            Some("The hash address of the contract package".to_string());
639        schema_object.into()
640    }
641}
642
643/// A enum to determine the lock status of the contract package.
644#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
645#[cfg_attr(feature = "datasize", derive(DataSize))]
646#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
647pub enum ContractPackageStatus {
648    /// The package is locked and cannot be versioned.
649    Locked,
650    /// The package is unlocked and can be versioned.
651    Unlocked,
652}
653
654impl ContractPackageStatus {
655    /// Create a new status flag based on a boolean value
656    pub fn new(is_locked: bool) -> Self {
657        if is_locked {
658            ContractPackageStatus::Locked
659        } else {
660            ContractPackageStatus::Unlocked
661        }
662    }
663}
664
665impl Default for ContractPackageStatus {
666    fn default() -> Self {
667        Self::Unlocked
668    }
669}
670
671impl ToBytes for ContractPackageStatus {
672    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
673        let mut result = bytesrepr::allocate_buffer(self)?;
674        match self {
675            ContractPackageStatus::Unlocked => result.append(&mut false.to_bytes()?),
676            ContractPackageStatus::Locked => result.append(&mut true.to_bytes()?),
677        }
678        Ok(result)
679    }
680
681    fn serialized_length(&self) -> usize {
682        match self {
683            ContractPackageStatus::Unlocked => false.serialized_length(),
684            ContractPackageStatus::Locked => true.serialized_length(),
685        }
686    }
687
688    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
689        match self {
690            ContractPackageStatus::Locked => writer.push(u8::from(true)),
691            ContractPackageStatus::Unlocked => writer.push(u8::from(false)),
692        }
693        Ok(())
694    }
695}
696
697impl FromBytes for ContractPackageStatus {
698    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
699        let (val, bytes) = bool::from_bytes(bytes)?;
700        let status = ContractPackageStatus::new(val);
701        Ok((status, bytes))
702    }
703}
704
705/// Contract definition, metadata, and security container.
706#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
707#[cfg_attr(feature = "datasize", derive(DataSize))]
708pub struct ContractPackage {
709    /// Key used to add or disable versions
710    access_key: URef,
711    /// All versions (enabled & disabled)
712    versions: ContractVersions,
713    /// Disabled versions
714    disabled_versions: DisabledVersions,
715    /// Mapping maintaining the set of URefs associated with each "user
716    /// group". This can be used to control access to methods in a particular
717    /// version of the contract. A method is callable by any context which
718    /// "knows" any of the URefs associated with the method's user group.
719    groups: Groups,
720    /// A flag that determines whether a contract is locked
721    lock_status: ContractPackageStatus,
722}
723
724impl CLTyped for ContractPackage {
725    fn cl_type() -> CLType {
726        CLType::Any
727    }
728}
729
730impl ContractPackage {
731    /// Create new `ContractPackage` (with no versions) from given access key.
732    pub fn new(
733        access_key: URef,
734        versions: ContractVersions,
735        disabled_versions: DisabledVersions,
736        groups: Groups,
737        lock_status: ContractPackageStatus,
738    ) -> Self {
739        ContractPackage {
740            access_key,
741            versions,
742            disabled_versions,
743            groups,
744            lock_status,
745        }
746    }
747
748    /// Get the access key for this contract.
749    pub fn access_key(&self) -> URef {
750        self.access_key
751    }
752
753    /// Get the mutable group definitions for this contract.
754    pub fn groups_mut(&mut self) -> &mut Groups {
755        &mut self.groups
756    }
757
758    /// Get the group definitions for this contract.
759    pub fn groups(&self) -> &Groups {
760        &self.groups
761    }
762
763    /// Adds new group to this contract.
764    pub fn add_group(&mut self, group: Group, urefs: BTreeSet<URef>) {
765        let v = self.groups.entry(group).or_default();
766        v.extend(urefs)
767    }
768
769    /// Lookup the contract hash for a given contract version (if present)
770    pub fn lookup_contract_hash(
771        &self,
772        contract_version_key: ContractVersionKey,
773    ) -> Option<&ContractHash> {
774        if !self.is_version_enabled(contract_version_key) {
775            return None;
776        }
777        self.versions.get(&contract_version_key)
778    }
779
780    /// Returns `true` if the given contract version exists and is enabled.
781    pub fn is_version_enabled(&self, contract_version_key: ContractVersionKey) -> bool {
782        !self.disabled_versions.contains(&contract_version_key)
783            && self.versions.contains_key(&contract_version_key)
784    }
785
786    /// Returns `true` if the given contract hash exists and is enabled.
787    pub fn is_contract_enabled(&self, contract_hash: &ContractHash) -> bool {
788        match self.find_contract_version_key_by_hash(contract_hash) {
789            Some(version_key) => !self.disabled_versions.contains(version_key),
790            None => false,
791        }
792    }
793
794    /// Insert a new contract version; the next sequential version number will be issued.
795    pub fn insert_contract_version(
796        &mut self,
797        protocol_version_major: ProtocolVersionMajor,
798        contract_hash: ContractHash,
799    ) -> ContractVersionKey {
800        let contract_version = self.next_contract_version_for(protocol_version_major);
801        let key = ContractVersionKey::new(protocol_version_major, contract_version);
802        self.versions.insert(key, contract_hash);
803        key
804    }
805
806    /// Disable the contract version corresponding to the given hash (if it exists).
807    pub fn disable_contract_version(&mut self, contract_hash: ContractHash) -> Result<(), Error> {
808        let contract_version_key = self
809            .find_contract_version_key_by_hash(&contract_hash)
810            .copied()
811            .ok_or(Error::ContractNotFound)?;
812
813        if !self.disabled_versions.contains(&contract_version_key) {
814            self.disabled_versions.insert(contract_version_key);
815        }
816
817        Ok(())
818    }
819
820    /// Enable the contract version corresponding to the given hash (if it exists).
821    pub fn enable_contract_version(&mut self, contract_hash: ContractHash) -> Result<(), Error> {
822        let contract_version_key = self
823            .find_contract_version_key_by_hash(&contract_hash)
824            .copied()
825            .ok_or(Error::ContractNotFound)?;
826
827        self.disabled_versions.remove(&contract_version_key);
828
829        Ok(())
830    }
831
832    fn find_contract_version_key_by_hash(
833        &self,
834        contract_hash: &ContractHash,
835    ) -> Option<&ContractVersionKey> {
836        self.versions
837            .iter()
838            .filter_map(|(k, v)| if v == contract_hash { Some(k) } else { None })
839            .next()
840    }
841
842    /// Returns reference to all of this contract's versions.
843    pub fn versions(&self) -> &ContractVersions {
844        &self.versions
845    }
846
847    /// Returns all of this contract's enabled contract versions.
848    pub fn enabled_versions(&self) -> ContractVersions {
849        let mut ret = ContractVersions::new();
850        for version in &self.versions {
851            if !self.is_version_enabled(*version.0) {
852                continue;
853            }
854            ret.insert(*version.0, *version.1);
855        }
856        ret
857    }
858
859    /// Returns mutable reference to all of this contract's versions (enabled and disabled).
860    pub fn versions_mut(&mut self) -> &mut ContractVersions {
861        &mut self.versions
862    }
863
864    /// Consumes the object and returns all of this contract's versions (enabled and disabled).
865    pub fn take_versions(self) -> ContractVersions {
866        self.versions
867    }
868
869    /// Returns all of this contract's disabled versions.
870    pub fn disabled_versions(&self) -> &DisabledVersions {
871        &self.disabled_versions
872    }
873
874    /// Returns mut reference to all of this contract's disabled versions.
875    pub fn disabled_versions_mut(&mut self) -> &mut DisabledVersions {
876        &mut self.disabled_versions
877    }
878
879    /// Removes a group from this contract (if it exists).
880    pub fn remove_group(&mut self, group: &Group) -> bool {
881        self.groups.remove(group).is_some()
882    }
883
884    /// Gets the next available contract version for the given protocol version
885    fn next_contract_version_for(&self, protocol_version: ProtocolVersionMajor) -> ContractVersion {
886        let current_version = self
887            .versions
888            .keys()
889            .rev()
890            .find_map(|&contract_version_key| {
891                if contract_version_key.protocol_version_major() == protocol_version {
892                    Some(contract_version_key.contract_version())
893                } else {
894                    None
895                }
896            })
897            .unwrap_or(0);
898
899        current_version + 1
900    }
901
902    /// Return the contract version key for the newest enabled contract version.
903    pub fn current_contract_version(&self) -> Option<ContractVersionKey> {
904        self.enabled_versions().keys().next_back().copied()
905    }
906
907    /// Return the contract hash for the newest enabled contract version.
908    pub fn current_contract_hash(&self) -> Option<ContractHash> {
909        self.enabled_versions().values().next_back().copied()
910    }
911
912    /// Return the lock status of the contract package.
913    pub fn is_locked(&self) -> bool {
914        match self.lock_status {
915            ContractPackageStatus::Unlocked => false,
916            ContractPackageStatus::Locked => true,
917        }
918    }
919
920    /// Return the package status itself
921    pub fn get_lock_status(&self) -> ContractPackageStatus {
922        self.lock_status.clone()
923    }
924}
925
926impl ToBytes for ContractPackage {
927    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
928        let mut result = bytesrepr::allocate_buffer(self)?;
929        self.access_key().write_bytes(&mut result)?;
930        self.versions().write_bytes(&mut result)?;
931        self.disabled_versions().write_bytes(&mut result)?;
932        self.groups().write_bytes(&mut result)?;
933        self.lock_status.write_bytes(&mut result)?;
934        Ok(result)
935    }
936
937    fn serialized_length(&self) -> usize {
938        self.access_key.serialized_length()
939            + self.versions.serialized_length()
940            + self.disabled_versions.serialized_length()
941            + self.groups.serialized_length()
942            + self.lock_status.serialized_length()
943    }
944
945    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
946        self.access_key().write_bytes(writer)?;
947        self.versions().write_bytes(writer)?;
948        self.disabled_versions().write_bytes(writer)?;
949        self.groups().write_bytes(writer)?;
950        self.lock_status.write_bytes(writer)?;
951        Ok(())
952    }
953}
954
955impl FromBytes for ContractPackage {
956    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
957        let (access_key, bytes) = URef::from_bytes(bytes)?;
958        let (versions, bytes) = ContractVersions::from_bytes(bytes)?;
959        let (disabled_versions, bytes) = DisabledVersions::from_bytes(bytes)?;
960        let (groups, bytes) = Groups::from_bytes(bytes)?;
961        let (lock_status, bytes) = ContractPackageStatus::from_bytes(bytes)?;
962        let result = ContractPackage {
963            access_key,
964            versions,
965            disabled_versions,
966            groups,
967            lock_status,
968        };
969
970        Ok((result, bytes))
971    }
972}
973
974/// Type alias for a container used inside [`EntryPoints`].
975pub type EntryPointsMap = BTreeMap<String, EntryPoint>;
976
977/// Collection of named entry points
978#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
979#[cfg_attr(feature = "datasize", derive(DataSize))]
980pub struct EntryPoints(EntryPointsMap);
981
982impl Default for EntryPoints {
983    fn default() -> Self {
984        let mut entry_points = EntryPoints::new();
985        let entry_point = EntryPoint::default();
986        entry_points.add_entry_point(entry_point);
987        entry_points
988    }
989}
990
991impl ToBytes for EntryPoints {
992    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
993        self.0.to_bytes()
994    }
995    fn serialized_length(&self) -> usize {
996        self.0.serialized_length()
997    }
998
999    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
1000        self.0.write_bytes(writer)?;
1001        Ok(())
1002    }
1003}
1004
1005impl FromBytes for EntryPoints {
1006    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1007        let (entry_points_map, rem) = EntryPointsMap::from_bytes(bytes)?;
1008        Ok((EntryPoints(entry_points_map), rem))
1009    }
1010}
1011
1012impl EntryPoints {
1013    /// Creates empty instance of [`EntryPoints`].
1014    pub fn new() -> EntryPoints {
1015        EntryPoints(EntryPointsMap::new())
1016    }
1017
1018    /// Adds new [`EntryPoint`].
1019    pub fn add_entry_point(&mut self, entry_point: EntryPoint) {
1020        self.0.insert(entry_point.name().to_string(), entry_point);
1021    }
1022
1023    /// Checks if given [`EntryPoint`] exists.
1024    pub fn has_entry_point(&self, entry_point_name: &str) -> bool {
1025        self.0.contains_key(entry_point_name)
1026    }
1027
1028    /// Gets an existing [`EntryPoint`] by its name.
1029    pub fn get(&self, entry_point_name: &str) -> Option<&EntryPoint> {
1030        self.0.get(entry_point_name)
1031    }
1032
1033    /// Returns iterator for existing entry point names.
1034    pub fn keys(&self) -> impl Iterator<Item = &String> {
1035        self.0.keys()
1036    }
1037
1038    /// Takes all entry points.
1039    pub fn take_entry_points(self) -> Vec<EntryPoint> {
1040        self.0.into_values().collect()
1041    }
1042
1043    /// Returns the length of the entry points
1044    pub fn len(&self) -> usize {
1045        self.0.len()
1046    }
1047
1048    /// Checks if the `EntryPoints` is empty.
1049    pub fn is_empty(&self) -> bool {
1050        self.0.is_empty()
1051    }
1052}
1053
1054impl From<Vec<EntryPoint>> for EntryPoints {
1055    fn from(entry_points: Vec<EntryPoint>) -> EntryPoints {
1056        let entries = entry_points
1057            .into_iter()
1058            .map(|entry_point| (String::from(entry_point.name()), entry_point))
1059            .collect();
1060        EntryPoints(entries)
1061    }
1062}
1063
1064/// Collection of named keys
1065pub type NamedKeys = BTreeMap<String, Key>;
1066
1067/// Methods and type signatures supported by a contract.
1068#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
1069#[cfg_attr(feature = "datasize", derive(DataSize))]
1070pub struct Contract {
1071    contract_package_hash: ContractPackageHash,
1072    contract_wasm_hash: ContractWasmHash,
1073    named_keys: NamedKeys,
1074    entry_points: EntryPoints,
1075    protocol_version: ProtocolVersion,
1076}
1077
1078impl From<Contract>
1079    for (
1080        ContractPackageHash,
1081        ContractWasmHash,
1082        NamedKeys,
1083        EntryPoints,
1084        ProtocolVersion,
1085    )
1086{
1087    fn from(contract: Contract) -> Self {
1088        (
1089            contract.contract_package_hash,
1090            contract.contract_wasm_hash,
1091            contract.named_keys,
1092            contract.entry_points,
1093            contract.protocol_version,
1094        )
1095    }
1096}
1097
1098impl Contract {
1099    /// `Contract` constructor.
1100    pub fn new(
1101        contract_package_hash: ContractPackageHash,
1102        contract_wasm_hash: ContractWasmHash,
1103        named_keys: NamedKeys,
1104        entry_points: EntryPoints,
1105        protocol_version: ProtocolVersion,
1106    ) -> Self {
1107        Contract {
1108            contract_package_hash,
1109            contract_wasm_hash,
1110            named_keys,
1111            entry_points,
1112            protocol_version,
1113        }
1114    }
1115
1116    /// Hash for accessing contract package
1117    pub fn contract_package_hash(&self) -> ContractPackageHash {
1118        self.contract_package_hash
1119    }
1120
1121    /// Hash for accessing contract WASM
1122    pub fn contract_wasm_hash(&self) -> ContractWasmHash {
1123        self.contract_wasm_hash
1124    }
1125
1126    /// Checks whether there is a method with the given name
1127    pub fn has_entry_point(&self, name: &str) -> bool {
1128        self.entry_points.has_entry_point(name)
1129    }
1130
1131    /// Returns the type signature for the given `method`.
1132    pub fn entry_point(&self, method: &str) -> Option<&EntryPoint> {
1133        self.entry_points.get(method)
1134    }
1135
1136    /// Get the protocol version this header is targeting.
1137    pub fn protocol_version(&self) -> ProtocolVersion {
1138        self.protocol_version
1139    }
1140
1141    /// Adds new entry point
1142    pub fn add_entry_point<T: Into<String>>(&mut self, entry_point: EntryPoint) {
1143        self.entry_points.add_entry_point(entry_point);
1144    }
1145
1146    /// Hash for accessing contract bytes
1147    pub fn contract_wasm_key(&self) -> Key {
1148        self.contract_wasm_hash.into()
1149    }
1150
1151    /// Returns immutable reference to methods
1152    pub fn entry_points(&self) -> &EntryPoints {
1153        &self.entry_points
1154    }
1155
1156    /// Takes `named_keys`
1157    pub fn take_named_keys(self) -> NamedKeys {
1158        self.named_keys
1159    }
1160
1161    /// Returns a reference to `named_keys`
1162    pub fn named_keys(&self) -> &NamedKeys {
1163        &self.named_keys
1164    }
1165
1166    /// Appends `keys` to `named_keys`
1167    pub fn named_keys_append(&mut self, keys: &mut NamedKeys) {
1168        self.named_keys.append(keys);
1169    }
1170
1171    /// Removes given named key.
1172    pub fn remove_named_key(&mut self, key: &str) -> Option<Key> {
1173        self.named_keys.remove(key)
1174    }
1175
1176    /// Clears the named keys for the contract.
1177    pub fn clear_named_keys(&mut self) {
1178        self.named_keys = NamedKeys::new();
1179    }
1180
1181    /// Set protocol_version.
1182    pub fn set_protocol_version(&mut self, protocol_version: ProtocolVersion) {
1183        self.protocol_version = protocol_version;
1184    }
1185
1186    /// Determines if `Contract` is compatible with a given `ProtocolVersion`.
1187    pub fn is_compatible_protocol_version(&self, protocol_version: ProtocolVersion) -> bool {
1188        self.protocol_version.value().major == protocol_version.value().major
1189    }
1190
1191    /// Extracts the access rights from the named keys of the contract.
1192    pub fn extract_access_rights(&self, contract_hash: ContractHash) -> ContextAccessRights {
1193        let urefs_iter = self
1194            .named_keys
1195            .values()
1196            .filter_map(|key| key.as_uref().copied());
1197        ContextAccessRights::new(contract_hash.into(), urefs_iter)
1198    }
1199}
1200
1201impl ToBytes for Contract {
1202    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1203        let mut result = bytesrepr::allocate_buffer(self)?;
1204        self.contract_package_hash().write_bytes(&mut result)?;
1205        self.contract_wasm_hash().write_bytes(&mut result)?;
1206        self.named_keys().write_bytes(&mut result)?;
1207        self.entry_points().write_bytes(&mut result)?;
1208        self.protocol_version().write_bytes(&mut result)?;
1209        Ok(result)
1210    }
1211
1212    fn serialized_length(&self) -> usize {
1213        ToBytes::serialized_length(&self.entry_points)
1214            + ToBytes::serialized_length(&self.contract_package_hash)
1215            + ToBytes::serialized_length(&self.contract_wasm_hash)
1216            + ToBytes::serialized_length(&self.protocol_version)
1217            + ToBytes::serialized_length(&self.named_keys)
1218    }
1219
1220    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
1221        self.contract_package_hash().write_bytes(writer)?;
1222        self.contract_wasm_hash().write_bytes(writer)?;
1223        self.named_keys().write_bytes(writer)?;
1224        self.entry_points().write_bytes(writer)?;
1225        self.protocol_version().write_bytes(writer)?;
1226        Ok(())
1227    }
1228}
1229
1230impl FromBytes for Contract {
1231    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1232        let (contract_package_hash, bytes) = FromBytes::from_bytes(bytes)?;
1233        let (contract_wasm_hash, bytes) = FromBytes::from_bytes(bytes)?;
1234        let (named_keys, bytes) = NamedKeys::from_bytes(bytes)?;
1235        let (entry_points, bytes) = EntryPoints::from_bytes(bytes)?;
1236        let (protocol_version, bytes) = ProtocolVersion::from_bytes(bytes)?;
1237        Ok((
1238            Contract {
1239                contract_package_hash,
1240                contract_wasm_hash,
1241                named_keys,
1242                entry_points,
1243                protocol_version,
1244            },
1245            bytes,
1246        ))
1247    }
1248}
1249
1250impl Default for Contract {
1251    fn default() -> Self {
1252        Contract {
1253            named_keys: NamedKeys::default(),
1254            entry_points: EntryPoints::default(),
1255            contract_wasm_hash: [0; KEY_HASH_LENGTH].into(),
1256            contract_package_hash: [0; KEY_HASH_LENGTH].into(),
1257            protocol_version: ProtocolVersion::V1_0_0,
1258        }
1259    }
1260}
1261
1262/// Context of method execution
1263#[repr(u8)]
1264#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
1265#[cfg_attr(feature = "datasize", derive(DataSize))]
1266#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
1267pub enum EntryPointType {
1268    /// Runs as session code
1269    Session = 0,
1270    /// Runs within contract's context
1271    Contract = 1,
1272}
1273
1274impl ToBytes for EntryPointType {
1275    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1276        (*self as u8).to_bytes()
1277    }
1278
1279    fn serialized_length(&self) -> usize {
1280        1
1281    }
1282
1283    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
1284        writer.push(*self as u8);
1285        Ok(())
1286    }
1287}
1288
1289impl FromBytes for EntryPointType {
1290    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1291        let (value, bytes) = u8::from_bytes(bytes)?;
1292        match value {
1293            0 => Ok((EntryPointType::Session, bytes)),
1294            1 => Ok((EntryPointType::Contract, bytes)),
1295            _ => Err(bytesrepr::Error::Formatting),
1296        }
1297    }
1298}
1299
1300/// Default name for an entry point
1301pub const DEFAULT_ENTRY_POINT_NAME: &str = "call";
1302
1303/// Default name for an installer entry point
1304pub const ENTRY_POINT_NAME_INSTALL: &str = "install";
1305
1306/// Default name for an upgrade entry point
1307pub const UPGRADE_ENTRY_POINT_NAME: &str = "upgrade";
1308
1309/// Collection of entry point parameters.
1310pub type Parameters = Vec<Parameter>;
1311
1312/// Type signature of a method. Order of arguments matter since can be
1313/// referenced by index as well as name.
1314#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1315#[cfg_attr(feature = "datasize", derive(DataSize))]
1316#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
1317pub struct EntryPoint {
1318    name: String,
1319    args: Parameters,
1320    ret: CLType,
1321    access: EntryPointAccess,
1322    entry_point_type: EntryPointType,
1323}
1324
1325impl From<EntryPoint> for (String, Parameters, CLType, EntryPointAccess, EntryPointType) {
1326    fn from(entry_point: EntryPoint) -> Self {
1327        (
1328            entry_point.name,
1329            entry_point.args,
1330            entry_point.ret,
1331            entry_point.access,
1332            entry_point.entry_point_type,
1333        )
1334    }
1335}
1336
1337impl EntryPoint {
1338    /// `EntryPoint` constructor.
1339    pub fn new<T: Into<String>>(
1340        name: T,
1341        args: Parameters,
1342        ret: CLType,
1343        access: EntryPointAccess,
1344        entry_point_type: EntryPointType,
1345    ) -> Self {
1346        EntryPoint {
1347            name: name.into(),
1348            args,
1349            ret,
1350            access,
1351            entry_point_type,
1352        }
1353    }
1354
1355    /// Create a default [`EntryPoint`] with specified name.
1356    pub fn default_with_name<T: Into<String>>(name: T) -> Self {
1357        EntryPoint {
1358            name: name.into(),
1359            ..Default::default()
1360        }
1361    }
1362
1363    /// Get name.
1364    pub fn name(&self) -> &str {
1365        &self.name
1366    }
1367
1368    /// Get access enum.
1369    pub fn access(&self) -> &EntryPointAccess {
1370        &self.access
1371    }
1372
1373    /// Get the arguments for this method.
1374    pub fn args(&self) -> &[Parameter] {
1375        self.args.as_slice()
1376    }
1377
1378    /// Get the return type.
1379    pub fn ret(&self) -> &CLType {
1380        &self.ret
1381    }
1382
1383    /// Obtains entry point
1384    pub fn entry_point_type(&self) -> EntryPointType {
1385        self.entry_point_type
1386    }
1387}
1388
1389impl Default for EntryPoint {
1390    /// constructor for a public session `EntryPoint` that takes no args and returns `Unit`
1391    fn default() -> Self {
1392        EntryPoint {
1393            name: DEFAULT_ENTRY_POINT_NAME.to_string(),
1394            args: Vec::new(),
1395            ret: CLType::Unit,
1396            access: EntryPointAccess::Public,
1397            entry_point_type: EntryPointType::Session,
1398        }
1399    }
1400}
1401
1402impl ToBytes for EntryPoint {
1403    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1404        let mut result = bytesrepr::allocate_buffer(self)?;
1405        result.append(&mut self.name.to_bytes()?);
1406        result.append(&mut self.args.to_bytes()?);
1407        self.ret.append_bytes(&mut result)?;
1408        result.append(&mut self.access.to_bytes()?);
1409        result.append(&mut self.entry_point_type.to_bytes()?);
1410
1411        Ok(result)
1412    }
1413
1414    fn serialized_length(&self) -> usize {
1415        self.name.serialized_length()
1416            + self.args.serialized_length()
1417            + self.ret.serialized_length()
1418            + self.access.serialized_length()
1419            + self.entry_point_type.serialized_length()
1420    }
1421
1422    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
1423        self.name().write_bytes(writer)?;
1424        self.args.write_bytes(writer)?;
1425        self.ret.append_bytes(writer)?;
1426        self.access().write_bytes(writer)?;
1427        self.entry_point_type().write_bytes(writer)?;
1428        Ok(())
1429    }
1430}
1431
1432impl FromBytes for EntryPoint {
1433    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1434        let (name, bytes) = String::from_bytes(bytes)?;
1435        let (args, bytes) = Vec::<Parameter>::from_bytes(bytes)?;
1436        let (ret, bytes) = CLType::from_bytes(bytes)?;
1437        let (access, bytes) = EntryPointAccess::from_bytes(bytes)?;
1438        let (entry_point_type, bytes) = EntryPointType::from_bytes(bytes)?;
1439
1440        Ok((
1441            EntryPoint {
1442                name,
1443                args,
1444                ret,
1445                access,
1446                entry_point_type,
1447            },
1448            bytes,
1449        ))
1450    }
1451}
1452
1453/// Enum describing the possible access control options for a contract entry
1454/// point (method).
1455#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1456#[cfg_attr(feature = "datasize", derive(DataSize))]
1457#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
1458pub enum EntryPointAccess {
1459    /// Anyone can call this method (no access controls).
1460    Public,
1461    /// Only users from the listed groups may call this method. Note: if the
1462    /// list is empty then this method is not callable from outside the
1463    /// contract.
1464    Groups(Vec<Group>),
1465}
1466
1467const ENTRYPOINTACCESS_PUBLIC_TAG: u8 = 1;
1468const ENTRYPOINTACCESS_GROUPS_TAG: u8 = 2;
1469
1470impl EntryPointAccess {
1471    /// Constructor for access granted to only listed groups.
1472    pub fn groups(labels: &[&str]) -> Self {
1473        let list: Vec<Group> = labels.iter().map(|s| Group(String::from(*s))).collect();
1474        EntryPointAccess::Groups(list)
1475    }
1476}
1477
1478impl ToBytes for EntryPointAccess {
1479    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1480        let mut result = bytesrepr::allocate_buffer(self)?;
1481
1482        match self {
1483            EntryPointAccess::Public => {
1484                result.push(ENTRYPOINTACCESS_PUBLIC_TAG);
1485            }
1486            EntryPointAccess::Groups(groups) => {
1487                result.push(ENTRYPOINTACCESS_GROUPS_TAG);
1488                result.append(&mut groups.to_bytes()?);
1489            }
1490        }
1491        Ok(result)
1492    }
1493
1494    fn serialized_length(&self) -> usize {
1495        match self {
1496            EntryPointAccess::Public => 1,
1497            EntryPointAccess::Groups(groups) => 1 + groups.serialized_length(),
1498        }
1499    }
1500
1501    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
1502        match self {
1503            EntryPointAccess::Public => {
1504                writer.push(ENTRYPOINTACCESS_PUBLIC_TAG);
1505            }
1506            EntryPointAccess::Groups(groups) => {
1507                writer.push(ENTRYPOINTACCESS_GROUPS_TAG);
1508                groups.write_bytes(writer)?;
1509            }
1510        }
1511        Ok(())
1512    }
1513}
1514
1515impl FromBytes for EntryPointAccess {
1516    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1517        let (tag, bytes) = u8::from_bytes(bytes)?;
1518
1519        match tag {
1520            ENTRYPOINTACCESS_PUBLIC_TAG => Ok((EntryPointAccess::Public, bytes)),
1521            ENTRYPOINTACCESS_GROUPS_TAG => {
1522                let (groups, bytes) = Vec::<Group>::from_bytes(bytes)?;
1523                let result = EntryPointAccess::Groups(groups);
1524                Ok((result, bytes))
1525            }
1526            _ => Err(bytesrepr::Error::Formatting),
1527        }
1528    }
1529}
1530
1531/// Parameter to a method
1532#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1533#[cfg_attr(feature = "datasize", derive(DataSize))]
1534#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
1535pub struct Parameter {
1536    name: String,
1537    cl_type: CLType,
1538}
1539
1540impl Parameter {
1541    /// `Parameter` constructor.
1542    pub fn new<T: Into<String>>(name: T, cl_type: CLType) -> Self {
1543        Parameter {
1544            name: name.into(),
1545            cl_type,
1546        }
1547    }
1548
1549    /// Get the type of this argument.
1550    pub fn cl_type(&self) -> &CLType {
1551        &self.cl_type
1552    }
1553
1554    /// Get a reference to the parameter's name.
1555    pub fn name(&self) -> &str {
1556        &self.name
1557    }
1558}
1559
1560impl From<Parameter> for (String, CLType) {
1561    fn from(parameter: Parameter) -> Self {
1562        (parameter.name, parameter.cl_type)
1563    }
1564}
1565
1566impl ToBytes for Parameter {
1567    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1568        let mut result = ToBytes::to_bytes(&self.name)?;
1569        self.cl_type.append_bytes(&mut result)?;
1570
1571        Ok(result)
1572    }
1573
1574    fn serialized_length(&self) -> usize {
1575        ToBytes::serialized_length(&self.name) + self.cl_type.serialized_length()
1576    }
1577
1578    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
1579        self.name.write_bytes(writer)?;
1580        self.cl_type.append_bytes(writer)
1581    }
1582}
1583
1584impl FromBytes for Parameter {
1585    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1586        let (name, bytes) = String::from_bytes(bytes)?;
1587        let (cl_type, bytes) = CLType::from_bytes(bytes)?;
1588
1589        Ok((Parameter { name, cl_type }, bytes))
1590    }
1591}
1592
1593#[cfg(test)]
1594mod tests {
1595    use std::iter::FromIterator;
1596
1597    use super::*;
1598    use crate::{AccessRights, URef, UREF_ADDR_LENGTH};
1599    use alloc::borrow::ToOwned;
1600
1601    const CONTRACT_HASH_V1: ContractHash = ContractHash::new([42; 32]);
1602    const CONTRACT_HASH_V2: ContractHash = ContractHash::new([84; 32]);
1603
1604    fn make_contract_package() -> ContractPackage {
1605        let mut contract_package = ContractPackage::new(
1606            URef::new([0; 32], AccessRights::NONE),
1607            ContractVersions::default(),
1608            DisabledVersions::default(),
1609            Groups::default(),
1610            ContractPackageStatus::default(),
1611        );
1612
1613        // add groups
1614        {
1615            let group_urefs = {
1616                let mut ret = BTreeSet::new();
1617                ret.insert(URef::new([1; 32], AccessRights::READ));
1618                ret
1619            };
1620
1621            contract_package
1622                .groups_mut()
1623                .insert(Group::new("Group 1"), group_urefs.clone());
1624
1625            contract_package
1626                .groups_mut()
1627                .insert(Group::new("Group 2"), group_urefs);
1628        }
1629
1630        // add entry_points
1631        let _entry_points = {
1632            let mut ret = BTreeMap::new();
1633            let entrypoint = EntryPoint::new(
1634                "method0".to_string(),
1635                vec![],
1636                CLType::U32,
1637                EntryPointAccess::groups(&["Group 2"]),
1638                EntryPointType::Session,
1639            );
1640            ret.insert(entrypoint.name().to_owned(), entrypoint);
1641            let entrypoint = EntryPoint::new(
1642                "method1".to_string(),
1643                vec![Parameter::new("Foo", CLType::U32)],
1644                CLType::U32,
1645                EntryPointAccess::groups(&["Group 1"]),
1646                EntryPointType::Session,
1647            );
1648            ret.insert(entrypoint.name().to_owned(), entrypoint);
1649            ret
1650        };
1651
1652        let _contract_package_hash = [41; 32];
1653        let _contract_wasm_hash = [43; 32];
1654        let _named_keys = NamedKeys::new();
1655        let protocol_version = ProtocolVersion::V1_0_0;
1656
1657        let v1 = contract_package
1658            .insert_contract_version(protocol_version.value().major, CONTRACT_HASH_V1);
1659        let v2 = contract_package
1660            .insert_contract_version(protocol_version.value().major, CONTRACT_HASH_V2);
1661
1662        assert!(v2 > v1);
1663
1664        contract_package
1665    }
1666
1667    #[test]
1668    fn next_contract_version() {
1669        let major = 1;
1670        let mut contract_package = ContractPackage::new(
1671            URef::new([0; 32], AccessRights::NONE),
1672            ContractVersions::default(),
1673            DisabledVersions::default(),
1674            Groups::default(),
1675            ContractPackageStatus::default(),
1676        );
1677        assert_eq!(contract_package.next_contract_version_for(major), 1);
1678
1679        let next_version = contract_package.insert_contract_version(major, [123; 32].into());
1680        assert_eq!(next_version, ContractVersionKey::new(major, 1));
1681        assert_eq!(contract_package.next_contract_version_for(major), 2);
1682        let next_version_2 = contract_package.insert_contract_version(major, [124; 32].into());
1683        assert_eq!(next_version_2, ContractVersionKey::new(major, 2));
1684
1685        let major = 2;
1686        assert_eq!(contract_package.next_contract_version_for(major), 1);
1687        let next_version_3 = contract_package.insert_contract_version(major, [42; 32].into());
1688        assert_eq!(next_version_3, ContractVersionKey::new(major, 1));
1689    }
1690
1691    #[test]
1692    fn roundtrip_serialization() {
1693        let contract_package = make_contract_package();
1694        let bytes = contract_package.to_bytes().expect("should serialize");
1695        let (decoded_package, rem) =
1696            ContractPackage::from_bytes(&bytes).expect("should deserialize");
1697        assert_eq!(contract_package, decoded_package);
1698        assert_eq!(rem.len(), 0);
1699    }
1700
1701    #[test]
1702    fn should_remove_group() {
1703        let mut contract_package = make_contract_package();
1704
1705        assert!(!contract_package.remove_group(&Group::new("Non-existent group")));
1706        assert!(contract_package.remove_group(&Group::new("Group 1")));
1707        assert!(!contract_package.remove_group(&Group::new("Group 1"))); // Group no longer exists
1708    }
1709
1710    #[test]
1711    fn should_disable_and_enable_contract_version() {
1712        const NEW_CONTRACT_HASH: ContractHash = ContractHash::new([123; 32]);
1713
1714        let mut contract_package = make_contract_package();
1715
1716        assert!(
1717            !contract_package.is_contract_enabled(&NEW_CONTRACT_HASH),
1718            "nonexisting contract contract should return false"
1719        );
1720
1721        assert_eq!(
1722            contract_package.current_contract_version(),
1723            Some(ContractVersionKey(1, 2))
1724        );
1725        assert_eq!(
1726            contract_package.current_contract_hash(),
1727            Some(CONTRACT_HASH_V2)
1728        );
1729
1730        assert_eq!(
1731            contract_package.versions(),
1732            &BTreeMap::from_iter([
1733                (ContractVersionKey(1, 1), CONTRACT_HASH_V1),
1734                (ContractVersionKey(1, 2), CONTRACT_HASH_V2)
1735            ]),
1736        );
1737        assert_eq!(
1738            contract_package.enabled_versions(),
1739            BTreeMap::from_iter([
1740                (ContractVersionKey(1, 1), CONTRACT_HASH_V1),
1741                (ContractVersionKey(1, 2), CONTRACT_HASH_V2)
1742            ]),
1743        );
1744
1745        assert!(!contract_package.is_contract_enabled(&NEW_CONTRACT_HASH));
1746
1747        assert_eq!(
1748            contract_package.disable_contract_version(NEW_CONTRACT_HASH),
1749            Err(Error::ContractNotFound),
1750            "should return contract not found error"
1751        );
1752
1753        assert!(
1754            !contract_package.is_contract_enabled(&NEW_CONTRACT_HASH),
1755            "disabling missing contract shouldnt change outcome"
1756        );
1757
1758        let next_version = contract_package.insert_contract_version(1, NEW_CONTRACT_HASH);
1759        assert!(
1760            contract_package.is_version_enabled(next_version),
1761            "version should exist and be enabled"
1762        );
1763        assert!(
1764            contract_package.is_contract_enabled(&NEW_CONTRACT_HASH),
1765            "contract should be enabled"
1766        );
1767
1768        assert_eq!(
1769            contract_package.disable_contract_version(NEW_CONTRACT_HASH),
1770            Ok(()),
1771            "should be able to disable version"
1772        );
1773        assert!(!contract_package.is_contract_enabled(&NEW_CONTRACT_HASH));
1774
1775        assert_eq!(
1776            contract_package.lookup_contract_hash(next_version),
1777            None,
1778            "should not return disabled contract version"
1779        );
1780
1781        assert!(
1782            !contract_package.is_version_enabled(next_version),
1783            "version should not be enabled"
1784        );
1785
1786        assert_eq!(
1787            contract_package.current_contract_version(),
1788            Some(ContractVersionKey(1, 2))
1789        );
1790        assert_eq!(
1791            contract_package.current_contract_hash(),
1792            Some(CONTRACT_HASH_V2)
1793        );
1794        assert_eq!(
1795            contract_package.versions(),
1796            &BTreeMap::from_iter([
1797                (ContractVersionKey(1, 1), CONTRACT_HASH_V1),
1798                (ContractVersionKey(1, 2), CONTRACT_HASH_V2),
1799                (next_version, NEW_CONTRACT_HASH),
1800            ]),
1801        );
1802        assert_eq!(
1803            contract_package.enabled_versions(),
1804            BTreeMap::from_iter([
1805                (ContractVersionKey(1, 1), CONTRACT_HASH_V1),
1806                (ContractVersionKey(1, 2), CONTRACT_HASH_V2),
1807            ]),
1808        );
1809        assert_eq!(
1810            contract_package.disabled_versions(),
1811            &BTreeSet::from_iter([next_version]),
1812        );
1813
1814        assert_eq!(
1815            contract_package.current_contract_version(),
1816            Some(ContractVersionKey(1, 2))
1817        );
1818        assert_eq!(
1819            contract_package.current_contract_hash(),
1820            Some(CONTRACT_HASH_V2)
1821        );
1822
1823        assert_eq!(
1824            contract_package.disable_contract_version(CONTRACT_HASH_V2),
1825            Ok(()),
1826            "should be able to disable version 2"
1827        );
1828
1829        assert_eq!(
1830            contract_package.enabled_versions(),
1831            BTreeMap::from_iter([(ContractVersionKey(1, 1), CONTRACT_HASH_V1),]),
1832        );
1833
1834        assert_eq!(
1835            contract_package.current_contract_version(),
1836            Some(ContractVersionKey(1, 1))
1837        );
1838        assert_eq!(
1839            contract_package.current_contract_hash(),
1840            Some(CONTRACT_HASH_V1)
1841        );
1842
1843        assert_eq!(
1844            contract_package.disabled_versions(),
1845            &BTreeSet::from_iter([next_version, ContractVersionKey(1, 2)]),
1846        );
1847
1848        assert_eq!(
1849            contract_package.enable_contract_version(CONTRACT_HASH_V2),
1850            Ok(()),
1851        );
1852
1853        assert_eq!(
1854            contract_package.enabled_versions(),
1855            BTreeMap::from_iter([
1856                (ContractVersionKey(1, 1), CONTRACT_HASH_V1),
1857                (ContractVersionKey(1, 2), CONTRACT_HASH_V2),
1858            ]),
1859        );
1860
1861        assert_eq!(
1862            contract_package.disabled_versions(),
1863            &BTreeSet::from_iter([next_version])
1864        );
1865
1866        assert_eq!(
1867            contract_package.current_contract_hash(),
1868            Some(CONTRACT_HASH_V2)
1869        );
1870
1871        assert_eq!(
1872            contract_package.enable_contract_version(NEW_CONTRACT_HASH),
1873            Ok(()),
1874        );
1875
1876        assert_eq!(
1877            contract_package.enable_contract_version(NEW_CONTRACT_HASH),
1878            Ok(()),
1879            "enabling a contract twice should be a noop"
1880        );
1881
1882        assert_eq!(
1883            contract_package.enabled_versions(),
1884            BTreeMap::from_iter([
1885                (ContractVersionKey(1, 1), CONTRACT_HASH_V1),
1886                (ContractVersionKey(1, 2), CONTRACT_HASH_V2),
1887                (next_version, NEW_CONTRACT_HASH),
1888            ]),
1889        );
1890
1891        assert_eq!(contract_package.disabled_versions(), &BTreeSet::new(),);
1892
1893        assert_eq!(
1894            contract_package.current_contract_hash(),
1895            Some(NEW_CONTRACT_HASH)
1896        );
1897    }
1898
1899    #[test]
1900    fn should_not_allow_to_enable_non_existing_version() {
1901        let mut contract_package = make_contract_package();
1902
1903        assert_eq!(
1904            contract_package.enable_contract_version(ContractHash::default()),
1905            Err(Error::ContractNotFound),
1906        );
1907    }
1908
1909    #[test]
1910    fn contract_hash_from_slice() {
1911        let bytes: Vec<u8> = (0..32).collect();
1912        let contract_hash = HashAddr::try_from(&bytes[..]).expect("should create contract hash");
1913        let contract_hash = ContractHash::new(contract_hash);
1914        assert_eq!(&bytes, &contract_hash.as_bytes());
1915    }
1916
1917    #[test]
1918    fn contract_package_hash_from_slice() {
1919        let bytes: Vec<u8> = (0..32).collect();
1920        let contract_hash = HashAddr::try_from(&bytes[..]).expect("should create contract hash");
1921        let contract_hash = ContractPackageHash::new(contract_hash);
1922        assert_eq!(&bytes, &contract_hash.as_bytes());
1923    }
1924
1925    #[test]
1926    fn contract_hash_from_str() {
1927        let contract_hash = ContractHash([3; 32]);
1928        let encoded = contract_hash.to_formatted_string();
1929        let decoded = ContractHash::from_formatted_str(&encoded).unwrap();
1930        assert_eq!(contract_hash, decoded);
1931
1932        let invalid_prefix =
1933            "contract--0000000000000000000000000000000000000000000000000000000000000000";
1934        assert!(ContractHash::from_formatted_str(invalid_prefix).is_err());
1935
1936        let short_addr = "contract-00000000000000000000000000000000000000000000000000000000000000";
1937        assert!(ContractHash::from_formatted_str(short_addr).is_err());
1938
1939        let long_addr =
1940            "contract-000000000000000000000000000000000000000000000000000000000000000000";
1941        assert!(ContractHash::from_formatted_str(long_addr).is_err());
1942
1943        let invalid_hex =
1944            "contract-000000000000000000000000000000000000000000000000000000000000000g";
1945        assert!(ContractHash::from_formatted_str(invalid_hex).is_err());
1946    }
1947
1948    #[test]
1949    fn contract_package_hash_from_str() {
1950        let contract_package_hash = ContractPackageHash([3; 32]);
1951        let encoded = contract_package_hash.to_formatted_string();
1952        let decoded = ContractPackageHash::from_formatted_str(&encoded).unwrap();
1953        assert_eq!(contract_package_hash, decoded);
1954
1955        let invalid_prefix =
1956            "contract-package0000000000000000000000000000000000000000000000000000000000000000";
1957        assert!(matches!(
1958            ContractPackageHash::from_formatted_str(invalid_prefix).unwrap_err(),
1959            FromStrError::InvalidPrefix
1960        ));
1961
1962        let short_addr =
1963            "contract-package-00000000000000000000000000000000000000000000000000000000000000";
1964        assert!(matches!(
1965            ContractPackageHash::from_formatted_str(short_addr).unwrap_err(),
1966            FromStrError::Hash(_)
1967        ));
1968
1969        let long_addr =
1970            "contract-package-000000000000000000000000000000000000000000000000000000000000000000";
1971        assert!(matches!(
1972            ContractPackageHash::from_formatted_str(long_addr).unwrap_err(),
1973            FromStrError::Hash(_)
1974        ));
1975
1976        let invalid_hex =
1977            "contract-package-000000000000000000000000000000000000000000000000000000000000000g";
1978        assert!(matches!(
1979            ContractPackageHash::from_formatted_str(invalid_hex).unwrap_err(),
1980            FromStrError::Hex(_)
1981        ));
1982    }
1983
1984    #[test]
1985    fn contract_package_hash_from_legacy_str() {
1986        let contract_package_hash = ContractPackageHash([3; 32]);
1987        let hex_addr = contract_package_hash.to_string();
1988        let legacy_encoded = format!("contract-package-wasm{}", hex_addr);
1989        let decoded_from_legacy = ContractPackageHash::from_formatted_str(&legacy_encoded)
1990            .expect("should accept legacy prefixed string");
1991        assert_eq!(
1992            contract_package_hash, decoded_from_legacy,
1993            "decoded_from_legacy should equal decoded"
1994        );
1995
1996        let invalid_prefix =
1997            "contract-packagewasm0000000000000000000000000000000000000000000000000000000000000000";
1998        assert!(matches!(
1999            ContractPackageHash::from_formatted_str(invalid_prefix).unwrap_err(),
2000            FromStrError::InvalidPrefix
2001        ));
2002
2003        let short_addr =
2004            "contract-package-wasm00000000000000000000000000000000000000000000000000000000000000";
2005        assert!(matches!(
2006            ContractPackageHash::from_formatted_str(short_addr).unwrap_err(),
2007            FromStrError::Hash(_)
2008        ));
2009
2010        let long_addr =
2011            "contract-package-wasm000000000000000000000000000000000000000000000000000000000000000000";
2012        assert!(matches!(
2013            ContractPackageHash::from_formatted_str(long_addr).unwrap_err(),
2014            FromStrError::Hash(_)
2015        ));
2016
2017        let invalid_hex =
2018            "contract-package-wasm000000000000000000000000000000000000000000000000000000000000000g";
2019        assert!(matches!(
2020            ContractPackageHash::from_formatted_str(invalid_hex).unwrap_err(),
2021            FromStrError::Hex(_)
2022        ));
2023    }
2024
2025    #[test]
2026    fn contract_hash_serde_roundtrip() {
2027        let contract_hash = ContractHash([255; 32]);
2028        let serialized = bincode::serialize(&contract_hash).unwrap();
2029        let deserialized = bincode::deserialize(&serialized).unwrap();
2030        assert_eq!(contract_hash, deserialized)
2031    }
2032
2033    #[test]
2034    fn contract_hash_json_roundtrip() {
2035        let contract_hash = ContractHash([255; 32]);
2036        let json_string = serde_json::to_string_pretty(&contract_hash).unwrap();
2037        let decoded = serde_json::from_str(&json_string).unwrap();
2038        assert_eq!(contract_hash, decoded)
2039    }
2040
2041    #[test]
2042    fn contract_package_hash_serde_roundtrip() {
2043        let contract_hash = ContractPackageHash([255; 32]);
2044        let serialized = bincode::serialize(&contract_hash).unwrap();
2045        let deserialized = bincode::deserialize(&serialized).unwrap();
2046        assert_eq!(contract_hash, deserialized)
2047    }
2048
2049    #[test]
2050    fn contract_package_hash_json_roundtrip() {
2051        let contract_hash = ContractPackageHash([255; 32]);
2052        let json_string = serde_json::to_string_pretty(&contract_hash).unwrap();
2053        let decoded = serde_json::from_str(&json_string).unwrap();
2054        assert_eq!(contract_hash, decoded)
2055    }
2056
2057    #[test]
2058    fn should_extract_access_rights() {
2059        let contract_hash = ContractHash([255; 32]);
2060        let uref = URef::new([84; UREF_ADDR_LENGTH], AccessRights::READ_ADD);
2061        let uref_r = URef::new([42; UREF_ADDR_LENGTH], AccessRights::READ);
2062        let uref_a = URef::new([42; UREF_ADDR_LENGTH], AccessRights::ADD);
2063        let uref_w = URef::new([42; UREF_ADDR_LENGTH], AccessRights::WRITE);
2064        let mut named_keys = NamedKeys::new();
2065        named_keys.insert("a".to_string(), Key::URef(uref_r));
2066        named_keys.insert("b".to_string(), Key::URef(uref_a));
2067        named_keys.insert("c".to_string(), Key::URef(uref_w));
2068        named_keys.insert("d".to_string(), Key::URef(uref));
2069        let contract = Contract::new(
2070            ContractPackageHash::new([254; 32]),
2071            ContractWasmHash::new([253; 32]),
2072            named_keys,
2073            EntryPoints::default(),
2074            ProtocolVersion::V1_0_0,
2075        );
2076        let access_rights = contract.extract_access_rights(contract_hash);
2077        let expected_uref = URef::new([42; UREF_ADDR_LENGTH], AccessRights::READ_ADD_WRITE);
2078        assert!(
2079            access_rights.has_access_rights_to_uref(&uref),
2080            "urefs in named keys should be included in access rights"
2081        );
2082        assert!(
2083            access_rights.has_access_rights_to_uref(&expected_uref),
2084            "multiple access right bits to the same uref should coalesce"
2085        );
2086    }
2087}
2088
2089#[cfg(test)]
2090mod prop_tests {
2091    use proptest::prelude::*;
2092
2093    use crate::{bytesrepr, gens};
2094
2095    proptest! {
2096        // #![proptest_config(ProptestConfig {
2097        //     cases: 1024,
2098        //     .. ProptestConfig::default()
2099        // })]
2100
2101        #[test]
2102        fn test_value_contract(contract in gens::contract_arb()) {
2103            bytesrepr::test_serialization_roundtrip(&contract);
2104        }
2105
2106        #[test]
2107        fn test_value_contract_package(contract_pkg in gens::contract_package_arb()) {
2108            bytesrepr::test_serialization_roundtrip(&contract_pkg);
2109        }
2110    }
2111}