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)]
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(2, version.contract_version());
964                let entity_hash = EntityAddr::SmartContract(contract_hash.value());
965                (entity_version, entity_hash)
966            })
967            .collect();
968
969        let disabled_versions = value
970            .disabled_versions
971            .into_iter()
972            .map(|contract_versions| {
973                EntityVersionKey::new(
974                    contract_versions.protocol_version_major(),
975                    contract_versions.contract_version(),
976                )
977            })
978            .collect();
979
980        let lock_status = if value.lock_status == ContractPackageStatus::Locked {
981            PackageStatus::Locked
982        } else {
983            PackageStatus::Unlocked
984        };
985
986        Package::new(
987            versions.into(),
988            disabled_versions,
989            value.groups,
990            lock_status,
991        )
992    }
993}
994
995/// Type signature of a method. Order of arguments matter since can be
996/// referenced by index as well as name.
997#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
998#[cfg_attr(feature = "datasize", derive(DataSize))]
999#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
1000pub struct EntryPoint {
1001    name: String,
1002    args: Parameters,
1003    ret: CLType,
1004    access: EntryPointAccess,
1005    entry_point_type: EntryPointType,
1006}
1007
1008impl From<EntryPoint> for (String, Parameters, CLType, EntryPointAccess, EntryPointType) {
1009    fn from(entry_point: EntryPoint) -> Self {
1010        (
1011            entry_point.name,
1012            entry_point.args,
1013            entry_point.ret,
1014            entry_point.access,
1015            entry_point.entry_point_type,
1016        )
1017    }
1018}
1019
1020impl EntryPoint {
1021    /// `EntryPoint` constructor.
1022    pub fn new<T: Into<String>>(
1023        name: T,
1024        args: Parameters,
1025        ret: CLType,
1026        access: EntryPointAccess,
1027        entry_point_type: EntryPointType,
1028    ) -> Self {
1029        EntryPoint {
1030            name: name.into(),
1031            args,
1032            ret,
1033            access,
1034            entry_point_type,
1035        }
1036    }
1037
1038    /// Create a default [`EntryPoint`] with specified name.
1039    pub fn default_with_name<T: Into<String>>(name: T) -> Self {
1040        EntryPoint {
1041            name: name.into(),
1042            ..Default::default()
1043        }
1044    }
1045
1046    /// Get name.
1047    pub fn name(&self) -> &str {
1048        &self.name
1049    }
1050
1051    /// Get access enum.
1052    pub fn access(&self) -> &EntryPointAccess {
1053        &self.access
1054    }
1055
1056    /// Get the arguments for this method.
1057    pub fn args(&self) -> &[Parameter] {
1058        self.args.as_slice()
1059    }
1060
1061    /// Get the return type.
1062    pub fn ret(&self) -> &CLType {
1063        &self.ret
1064    }
1065
1066    /// Obtains entry point
1067    pub fn entry_point_type(&self) -> EntryPointType {
1068        self.entry_point_type
1069    }
1070}
1071
1072impl Default for EntryPoint {
1073    /// constructor for a public session `EntryPoint` that takes no args and returns `Unit`
1074    fn default() -> Self {
1075        EntryPoint {
1076            name: DEFAULT_ENTRY_POINT_NAME.to_string(),
1077            args: Vec::new(),
1078            ret: CLType::Unit,
1079            access: EntryPointAccess::Public,
1080            entry_point_type: EntryPointType::Caller,
1081        }
1082    }
1083}
1084
1085impl ToBytes for EntryPoint {
1086    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1087        let mut buffer = bytesrepr::allocate_buffer(self)?;
1088        self.write_bytes(&mut buffer)?;
1089        Ok(buffer)
1090    }
1091
1092    fn serialized_length(&self) -> usize {
1093        self.name.serialized_length()
1094            + self.args.serialized_length()
1095            + self.ret.serialized_length()
1096            + self.access.serialized_length()
1097            + self.entry_point_type.serialized_length()
1098    }
1099
1100    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
1101        self.name.write_bytes(writer)?;
1102        self.args.write_bytes(writer)?;
1103        self.ret.append_bytes(writer)?;
1104        self.access.write_bytes(writer)?;
1105        self.entry_point_type.write_bytes(writer)?;
1106        Ok(())
1107    }
1108}
1109
1110impl FromBytes for EntryPoint {
1111    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1112        let (name, bytes) = String::from_bytes(bytes)?;
1113        let (args, bytes) = Vec::<Parameter>::from_bytes(bytes)?;
1114        let (ret, bytes) = CLType::from_bytes(bytes)?;
1115        let (access, bytes) = EntryPointAccess::from_bytes(bytes)?;
1116        let (entry_point_type, bytes) = EntryPointType::from_bytes(bytes)?;
1117
1118        Ok((
1119            EntryPoint {
1120                name,
1121                args,
1122                ret,
1123                access,
1124                entry_point_type,
1125            },
1126            bytes,
1127        ))
1128    }
1129}
1130
1131/// Collection of named entry points.
1132#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
1133#[cfg_attr(feature = "datasize", derive(DataSize))]
1134#[serde(transparent, deny_unknown_fields)]
1135pub struct EntryPoints(BTreeMap<String, EntryPoint>);
1136
1137impl From<crate::addressable_entity::EntryPoints> for EntryPoints {
1138    fn from(value: EntityEntryPoints) -> Self {
1139        let mut ret = EntryPoints::new();
1140        for entity_entry_point in value.take_entry_points() {
1141            let entry_point = EntryPoint::new(
1142                entity_entry_point.name(),
1143                Parameters::from(entity_entry_point.args()),
1144                entity_entry_point.ret().clone(),
1145                entity_entry_point.access().clone(),
1146                entity_entry_point.entry_point_type(),
1147            );
1148            ret.add_entry_point(entry_point);
1149        }
1150        ret
1151    }
1152}
1153
1154impl ToBytes for EntryPoints {
1155    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1156        self.0.to_bytes()
1157    }
1158
1159    fn serialized_length(&self) -> usize {
1160        self.0.serialized_length()
1161    }
1162
1163    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
1164        self.0.write_bytes(writer)
1165    }
1166}
1167
1168impl FromBytes for EntryPoints {
1169    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1170        let (entry_points_map, remainder) = BTreeMap::<String, EntryPoint>::from_bytes(bytes)?;
1171        Ok((EntryPoints(entry_points_map), remainder))
1172    }
1173}
1174
1175impl Default for EntryPoints {
1176    fn default() -> Self {
1177        let mut entry_points = EntryPoints::new();
1178        let entry_point = EntryPoint::default();
1179        entry_points.add_entry_point(entry_point);
1180        entry_points
1181    }
1182}
1183
1184impl From<EntryPoint> for EntityEntryPoint {
1185    fn from(value: EntryPoint) -> Self {
1186        EntityEntryPoint::from(&value)
1187    }
1188}
1189
1190impl From<&EntryPoint> for EntityEntryPoint {
1191    fn from(value: &EntryPoint) -> Self {
1192        EntityEntryPoint::new(
1193            value.name.clone(),
1194            value.args.clone(),
1195            value.ret.clone(),
1196            value.access.clone(),
1197            value.entry_point_type,
1198            EntryPointPayment::Caller,
1199        )
1200    }
1201}
1202
1203impl EntryPoints {
1204    /// Constructs a new, empty `EntryPoints`.
1205    pub const fn new() -> EntryPoints {
1206        EntryPoints(BTreeMap::<String, EntryPoint>::new())
1207    }
1208
1209    /// Constructs a new `EntryPoints` with a single entry for the default `EntryPoint`.
1210    pub fn new_with_default_entry_point() -> Self {
1211        let mut entry_points = EntryPoints::new();
1212        let entry_point = EntryPoint::default();
1213        entry_points.add_entry_point(entry_point);
1214        entry_points
1215    }
1216
1217    /// Adds new [`EntryPoint`].
1218    pub fn add_entry_point(&mut self, entry_point: EntryPoint) -> Option<EntryPoint> {
1219        self.0.insert(entry_point.name().to_string(), entry_point)
1220    }
1221
1222    /// Checks if given [`EntryPoint`] exists.
1223    pub fn has_entry_point(&self, entry_point_name: &str) -> bool {
1224        self.0.contains_key(entry_point_name)
1225    }
1226
1227    /// Gets an existing [`EntryPoint`] by its name.
1228    pub fn get(&self, entry_point_name: &str) -> Option<&EntryPoint> {
1229        self.0.get(entry_point_name)
1230    }
1231
1232    /// Returns iterator for existing entry point names.
1233    pub fn keys(&self) -> impl Iterator<Item = &String> {
1234        self.0.keys()
1235    }
1236
1237    /// Takes all entry points.
1238    pub fn take_entry_points(self) -> Vec<EntryPoint> {
1239        self.0.into_values().collect()
1240    }
1241
1242    /// Returns the length of the entry points
1243    pub fn len(&self) -> usize {
1244        self.0.len()
1245    }
1246
1247    /// Checks if the `EntryPoints` is empty.
1248    pub fn is_empty(&self) -> bool {
1249        self.0.is_empty()
1250    }
1251
1252    /// Checks if any of the entry points are of the type Session.
1253    pub fn contains_stored_session(&self) -> bool {
1254        self.0
1255            .values()
1256            .any(|entry_point| entry_point.entry_point_type == EntryPointType::Caller)
1257    }
1258}
1259
1260impl From<Vec<EntryPoint>> for EntryPoints {
1261    fn from(entry_points: Vec<EntryPoint>) -> EntryPoints {
1262        let entries = entry_points
1263            .into_iter()
1264            .map(|entry_point| (String::from(entry_point.name()), entry_point))
1265            .collect();
1266        EntryPoints(entries)
1267    }
1268}
1269
1270impl From<EntryPoints> for EntityEntryPoints {
1271    fn from(value: EntryPoints) -> Self {
1272        let mut entry_points = EntityEntryPoints::new();
1273        for contract_entry_point in value.take_entry_points() {
1274            entry_points.add_entry_point(EntityEntryPoint::from(contract_entry_point));
1275        }
1276        entry_points
1277    }
1278}
1279
1280/// Methods and type signatures supported by a contract.
1281#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1282#[cfg_attr(feature = "datasize", derive(DataSize))]
1283#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
1284pub struct Contract {
1285    contract_package_hash: ContractPackageHash,
1286    contract_wasm_hash: ContractWasmHash,
1287    named_keys: NamedKeys,
1288    #[cfg_attr(feature = "json-schema", schemars(with = "Vec<EntryPoint>"))]
1289    entry_points: EntryPoints,
1290    protocol_version: ProtocolVersion,
1291}
1292
1293impl Contract {
1294    /// `Contract` constructor.
1295    pub fn new(
1296        contract_package_hash: ContractPackageHash,
1297        contract_wasm_hash: ContractWasmHash,
1298        named_keys: NamedKeys,
1299        entry_points: EntryPoints,
1300        protocol_version: ProtocolVersion,
1301    ) -> Self {
1302        Contract {
1303            contract_package_hash,
1304            contract_wasm_hash,
1305            named_keys,
1306            entry_points,
1307            protocol_version,
1308        }
1309    }
1310
1311    /// Hash for accessing contract package
1312    pub fn contract_package_hash(&self) -> ContractPackageHash {
1313        self.contract_package_hash
1314    }
1315
1316    /// Hash for accessing contract WASM
1317    pub fn contract_wasm_hash(&self) -> ContractWasmHash {
1318        self.contract_wasm_hash
1319    }
1320
1321    /// Checks whether there is a method with the given name
1322    pub fn has_entry_point(&self, name: &str) -> bool {
1323        self.entry_points.has_entry_point(name)
1324    }
1325
1326    /// Returns the type signature for the given `method`.
1327    pub fn entry_point(&self, method: &str) -> Option<&EntryPoint> {
1328        self.entry_points.get(method)
1329    }
1330
1331    /// Get the protocol version this header is targeting.
1332    pub fn protocol_version(&self) -> ProtocolVersion {
1333        self.protocol_version
1334    }
1335
1336    /// Adds new entry point
1337    pub fn add_entry_point<T: Into<String>>(&mut self, entry_point: EntryPoint) {
1338        self.entry_points.add_entry_point(entry_point);
1339    }
1340
1341    /// Hash for accessing contract bytes
1342    pub fn contract_wasm_key(&self) -> Key {
1343        self.contract_wasm_hash.into()
1344    }
1345
1346    /// Returns immutable reference to methods
1347    pub fn entry_points(&self) -> &EntryPoints {
1348        &self.entry_points
1349    }
1350
1351    /// Takes `named_keys`
1352    pub fn take_named_keys(self) -> NamedKeys {
1353        self.named_keys
1354    }
1355
1356    /// Returns a reference to `named_keys`
1357    pub fn named_keys(&self) -> &NamedKeys {
1358        &self.named_keys
1359    }
1360
1361    /// Appends `keys` to `named_keys`
1362    pub fn named_keys_append(&mut self, keys: NamedKeys) {
1363        self.named_keys.append(keys);
1364    }
1365
1366    /// Removes given named key.
1367    pub fn remove_named_key(&mut self, key: &str) -> Option<Key> {
1368        self.named_keys.remove(key)
1369    }
1370
1371    /// Set protocol_version.
1372    pub fn set_protocol_version(&mut self, protocol_version: ProtocolVersion) {
1373        self.protocol_version = protocol_version;
1374    }
1375
1376    /// Determines if `Contract` is compatible with a given `ProtocolVersion`.
1377    pub fn is_compatible_protocol_version(&self, protocol_version: ProtocolVersion) -> bool {
1378        self.protocol_version.value().major == protocol_version.value().major
1379    }
1380}
1381
1382impl ToBytes for Contract {
1383    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1384        let mut result = bytesrepr::allocate_buffer(self)?;
1385        self.contract_package_hash().write_bytes(&mut result)?;
1386        self.contract_wasm_hash().write_bytes(&mut result)?;
1387        self.named_keys().write_bytes(&mut result)?;
1388        self.entry_points().write_bytes(&mut result)?;
1389        self.protocol_version().write_bytes(&mut result)?;
1390        Ok(result)
1391    }
1392
1393    fn serialized_length(&self) -> usize {
1394        ToBytes::serialized_length(&self.entry_points)
1395            + ToBytes::serialized_length(&self.contract_package_hash)
1396            + ToBytes::serialized_length(&self.contract_wasm_hash)
1397            + ToBytes::serialized_length(&self.protocol_version)
1398            + ToBytes::serialized_length(&self.named_keys)
1399    }
1400
1401    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
1402        self.contract_package_hash().write_bytes(writer)?;
1403        self.contract_wasm_hash().write_bytes(writer)?;
1404        self.named_keys().write_bytes(writer)?;
1405        self.entry_points().write_bytes(writer)?;
1406        self.protocol_version().write_bytes(writer)?;
1407        Ok(())
1408    }
1409}
1410
1411impl FromBytes for Contract {
1412    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1413        let (contract_package_hash, bytes) = FromBytes::from_bytes(bytes)?;
1414        let (contract_wasm_hash, bytes) = FromBytes::from_bytes(bytes)?;
1415        let (named_keys, bytes) = NamedKeys::from_bytes(bytes)?;
1416        let (entry_points, bytes) = EntryPoints::from_bytes(bytes)?;
1417        let (protocol_version, bytes) = ProtocolVersion::from_bytes(bytes)?;
1418        Ok((
1419            Contract {
1420                contract_package_hash,
1421                contract_wasm_hash,
1422                named_keys,
1423                entry_points,
1424                protocol_version,
1425            },
1426            bytes,
1427        ))
1428    }
1429}
1430
1431impl Default for Contract {
1432    fn default() -> Self {
1433        Contract {
1434            named_keys: NamedKeys::default(),
1435            entry_points: EntryPoints::default(),
1436            contract_wasm_hash: [0; KEY_HASH_LENGTH].into(),
1437            contract_package_hash: [0; KEY_HASH_LENGTH].into(),
1438            protocol_version: ProtocolVersion::V1_0_0,
1439        }
1440    }
1441}
1442
1443/// Default name for an entry point
1444pub const DEFAULT_ENTRY_POINT_NAME: &str = "call";
1445
1446/// Default name for an installer entry point
1447pub const ENTRY_POINT_NAME_INSTALL: &str = "install";
1448
1449/// Default name for an upgrade entry point
1450pub const UPGRADE_ENTRY_POINT_NAME: &str = "upgrade";
1451
1452#[cfg(test)]
1453mod tests {
1454    use super::*;
1455    use crate::{AccessRights, EntryPointAccess, EntryPointType, Group, Parameter, URef};
1456    use alloc::borrow::ToOwned;
1457
1458    const CONTRACT_HASH_V1: ContractHash = ContractHash::new([42; 32]);
1459    const CONTRACT_HASH_V2: ContractHash = ContractHash::new([84; 32]);
1460
1461    fn make_contract_package() -> ContractPackage {
1462        let mut contract_package = ContractPackage::new(
1463            URef::new([0; 32], AccessRights::NONE),
1464            ContractVersions::default(),
1465            DisabledVersions::default(),
1466            Groups::default(),
1467            ContractPackageStatus::default(),
1468        );
1469
1470        // add groups
1471        {
1472            let group_urefs = {
1473                let mut ret = BTreeSet::new();
1474                ret.insert(URef::new([1; 32], AccessRights::READ));
1475                ret
1476            };
1477
1478            contract_package
1479                .groups_mut()
1480                .insert(Group::new("Group 1"), group_urefs.clone());
1481
1482            contract_package
1483                .groups_mut()
1484                .insert(Group::new("Group 2"), group_urefs);
1485        }
1486
1487        // add entry_points
1488        let _entry_points = {
1489            let mut ret = BTreeMap::new();
1490            let entrypoint = EntryPoint::new(
1491                "method0".to_string(),
1492                vec![],
1493                CLType::U32,
1494                EntryPointAccess::groups(&["Group 2"]),
1495                EntryPointType::Caller,
1496            );
1497            ret.insert(entrypoint.name().to_owned(), entrypoint);
1498            let entrypoint = EntryPoint::new(
1499                "method1".to_string(),
1500                vec![Parameter::new("Foo", CLType::U32)],
1501                CLType::U32,
1502                EntryPointAccess::groups(&["Group 1"]),
1503                EntryPointType::Caller,
1504            );
1505            ret.insert(entrypoint.name().to_owned(), entrypoint);
1506            ret
1507        };
1508
1509        let _contract_package_hash = [41; 32];
1510        let _contract_wasm_hash = [43; 32];
1511        let _named_keys = NamedKeys::new();
1512        let protocol_version = ProtocolVersion::V1_0_0;
1513
1514        let v1 = contract_package
1515            .insert_contract_version(protocol_version.value().major, CONTRACT_HASH_V1);
1516        let v2 = contract_package
1517            .insert_contract_version(protocol_version.value().major, CONTRACT_HASH_V2);
1518
1519        assert!(v2 > v1);
1520
1521        contract_package
1522    }
1523
1524    #[test]
1525    fn roundtrip_serialization() {
1526        let contract_package = make_contract_package();
1527        let bytes = contract_package.to_bytes().expect("should serialize");
1528        let (decoded_package, rem) =
1529            ContractPackage::from_bytes(&bytes).expect("should deserialize");
1530        assert_eq!(contract_package, decoded_package);
1531        assert_eq!(rem.len(), 0);
1532    }
1533
1534    #[test]
1535    fn contract_hash_from_slice() {
1536        let bytes: Vec<u8> = (0..32).collect();
1537        let contract_hash = HashAddr::try_from(&bytes[..]).expect("should create contract hash");
1538        let contract_hash = ContractHash::new(contract_hash);
1539        assert_eq!(&bytes, &contract_hash.as_bytes());
1540    }
1541
1542    #[test]
1543    fn contract_package_hash_from_slice() {
1544        let bytes: Vec<u8> = (0..32).collect();
1545        let contract_hash = HashAddr::try_from(&bytes[..]).expect("should create contract hash");
1546        let contract_hash = ContractPackageHash::new(contract_hash);
1547        assert_eq!(&bytes, &contract_hash.as_bytes());
1548    }
1549
1550    #[test]
1551    fn contract_hash_from_str() {
1552        let contract_hash = ContractHash([3; 32]);
1553        let encoded = contract_hash.to_formatted_string();
1554        let decoded = ContractHash::from_formatted_str(&encoded).unwrap();
1555        assert_eq!(contract_hash, decoded);
1556
1557        let invalid_prefix =
1558            "contract--0000000000000000000000000000000000000000000000000000000000000000";
1559        assert!(ContractHash::from_formatted_str(invalid_prefix).is_err());
1560
1561        let short_addr = "contract-00000000000000000000000000000000000000000000000000000000000000";
1562        assert!(ContractHash::from_formatted_str(short_addr).is_err());
1563
1564        let long_addr =
1565            "contract-000000000000000000000000000000000000000000000000000000000000000000";
1566        assert!(ContractHash::from_formatted_str(long_addr).is_err());
1567
1568        let invalid_hex =
1569            "contract-000000000000000000000000000000000000000000000000000000000000000g";
1570        assert!(ContractHash::from_formatted_str(invalid_hex).is_err());
1571    }
1572
1573    #[test]
1574    fn contract_package_hash_from_str() {
1575        let contract_package_hash = ContractPackageHash([3; 32]);
1576        let encoded = contract_package_hash.to_formatted_string();
1577        let decoded = ContractPackageHash::from_formatted_str(&encoded).unwrap();
1578        assert_eq!(contract_package_hash, decoded);
1579
1580        let invalid_prefix =
1581            "contract-package0000000000000000000000000000000000000000000000000000000000000000";
1582        assert!(matches!(
1583            ContractPackageHash::from_formatted_str(invalid_prefix).unwrap_err(),
1584            FromStrError::InvalidPrefix
1585        ));
1586
1587        let short_addr =
1588            "contract-package-00000000000000000000000000000000000000000000000000000000000000";
1589        assert!(matches!(
1590            ContractPackageHash::from_formatted_str(short_addr).unwrap_err(),
1591            FromStrError::Hash(_)
1592        ));
1593
1594        let long_addr =
1595            "contract-package-000000000000000000000000000000000000000000000000000000000000000000";
1596        assert!(matches!(
1597            ContractPackageHash::from_formatted_str(long_addr).unwrap_err(),
1598            FromStrError::Hash(_)
1599        ));
1600
1601        let invalid_hex =
1602            "contract-package-000000000000000000000000000000000000000000000000000000000000000g";
1603        assert!(matches!(
1604            ContractPackageHash::from_formatted_str(invalid_hex).unwrap_err(),
1605            FromStrError::Hex(_)
1606        ));
1607    }
1608
1609    #[test]
1610    fn contract_package_hash_from_legacy_str() {
1611        let contract_package_hash = ContractPackageHash([3; 32]);
1612        let hex_addr = contract_package_hash.to_string();
1613        let legacy_encoded = format!("contract-package-wasm{}", hex_addr);
1614        let decoded_from_legacy = ContractPackageHash::from_formatted_str(&legacy_encoded)
1615            .expect("should accept legacy prefixed string");
1616        assert_eq!(
1617            contract_package_hash, decoded_from_legacy,
1618            "decoded_from_legacy should equal decoded"
1619        );
1620
1621        let invalid_prefix =
1622            "contract-packagewasm0000000000000000000000000000000000000000000000000000000000000000";
1623        assert!(matches!(
1624            ContractPackageHash::from_formatted_str(invalid_prefix).unwrap_err(),
1625            FromStrError::InvalidPrefix
1626        ));
1627
1628        let short_addr =
1629            "contract-package-wasm00000000000000000000000000000000000000000000000000000000000000";
1630        assert!(matches!(
1631            ContractPackageHash::from_formatted_str(short_addr).unwrap_err(),
1632            FromStrError::Hash(_)
1633        ));
1634
1635        let long_addr =
1636            "contract-package-wasm000000000000000000000000000000000000000000000000000000000000000000";
1637        assert!(matches!(
1638            ContractPackageHash::from_formatted_str(long_addr).unwrap_err(),
1639            FromStrError::Hash(_)
1640        ));
1641
1642        let invalid_hex =
1643            "contract-package-wasm000000000000000000000000000000000000000000000000000000000000000g";
1644        assert!(matches!(
1645            ContractPackageHash::from_formatted_str(invalid_hex).unwrap_err(),
1646            FromStrError::Hex(_)
1647        ));
1648    }
1649
1650    #[test]
1651    fn contract_hash_serde_roundtrip() {
1652        let contract_hash = ContractHash([255; 32]);
1653        let serialized = bincode::serialize(&contract_hash).unwrap();
1654        let deserialized = bincode::deserialize(&serialized).unwrap();
1655        assert_eq!(contract_hash, deserialized)
1656    }
1657
1658    #[test]
1659    fn contract_hash_json_roundtrip() {
1660        let contract_hash = ContractHash([255; 32]);
1661        let json_string = serde_json::to_string_pretty(&contract_hash).unwrap();
1662        let decoded = serde_json::from_str(&json_string).unwrap();
1663        assert_eq!(contract_hash, decoded)
1664    }
1665
1666    #[test]
1667    fn contract_package_hash_serde_roundtrip() {
1668        let contract_hash = ContractPackageHash([255; 32]);
1669        let serialized = bincode::serialize(&contract_hash).unwrap();
1670        let deserialized = bincode::deserialize(&serialized).unwrap();
1671        assert_eq!(contract_hash, deserialized)
1672    }
1673
1674    #[test]
1675    fn contract_package_hash_json_roundtrip() {
1676        let contract_hash = ContractPackageHash([255; 32]);
1677        let json_string = serde_json::to_string_pretty(&contract_hash).unwrap();
1678        let decoded = serde_json::from_str(&json_string).unwrap();
1679        assert_eq!(contract_hash, decoded)
1680    }
1681
1682    #[test]
1683    fn package_hash_from_legacy_str() {
1684        let package_hash = ContractPackageHash([3; 32]);
1685        let hex_addr = package_hash.to_string();
1686        let legacy_encoded = format!("contract-package-wasm{}", hex_addr);
1687        let decoded_from_legacy = ContractPackageHash::from_formatted_str(&legacy_encoded)
1688            .expect("should accept legacy prefixed string");
1689        assert_eq!(
1690            package_hash, decoded_from_legacy,
1691            "decoded_from_legacy should equal decoded"
1692        );
1693
1694        let invalid_prefix =
1695            "contract-packagewasm0000000000000000000000000000000000000000000000000000000000000000";
1696        assert!(matches!(
1697            ContractPackageHash::from_formatted_str(invalid_prefix).unwrap_err(),
1698            FromStrError::InvalidPrefix
1699        ));
1700
1701        let short_addr =
1702            "contract-package-wasm00000000000000000000000000000000000000000000000000000000000000";
1703        assert!(matches!(
1704            ContractPackageHash::from_formatted_str(short_addr).unwrap_err(),
1705            FromStrError::Hash(_)
1706        ));
1707
1708        let long_addr =
1709            "contract-package-wasm000000000000000000000000000000000000000000000000000000000000000000";
1710        assert!(matches!(
1711            ContractPackageHash::from_formatted_str(long_addr).unwrap_err(),
1712            FromStrError::Hash(_)
1713        ));
1714
1715        let invalid_hex =
1716            "contract-package-wasm000000000000000000000000000000000000000000000000000000000000000g";
1717        assert!(matches!(
1718            ContractPackageHash::from_formatted_str(invalid_hex).unwrap_err(),
1719            FromStrError::Hex(_)
1720        ));
1721    }
1722}
1723
1724#[cfg(test)]
1725mod prop_tests {
1726    use proptest::prelude::*;
1727
1728    use crate::{bytesrepr, contracts::ContractPackage, gens};
1729
1730    proptest! {
1731        #![proptest_config(ProptestConfig {
1732            cases: 1024,
1733            .. ProptestConfig::default()
1734        })]
1735
1736        #[test]
1737        fn test_value_contract(contract in gens::contract_arb()) {
1738            bytesrepr::test_serialization_roundtrip(&contract);
1739        }
1740
1741        #[test]
1742        fn test_value_contract_package(contract_pkg in gens::contract_package_arb()) {
1743            bytesrepr::test_serialization_roundtrip(&contract_pkg);
1744        }
1745
1746        #[test]
1747        fn test_json_contract_package(v in gens::contract_package_arb()) {
1748            let json_str = serde_json::to_string(&v).unwrap();
1749            let deserialized = serde_json::from_str::<ContractPackage>(&json_str).unwrap();
1750            assert_eq!(v, deserialized);
1751        }
1752    }
1753}