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