1#![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-";
48const CONTRACT_PACKAGE_STRING_LEGACY_EXTRA_PREFIX: &str = "wasm";
50
51#[derive(Debug, PartialEq, Eq)]
53#[repr(u8)]
54#[non_exhaustive]
55pub enum Error {
56 PreviouslyUsedVersion = 1,
64 ContractNotFound = 2,
70 GroupAlreadyExists = 3,
77 MaxGroupsExceeded = 4,
84 MaxTotalURefsExceeded = 5,
91 GroupDoesNotExist = 6,
98 UnableToRemoveURef = 7,
104 GroupInUse = 8,
110 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#[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#[derive(Debug)]
150#[non_exhaustive]
151pub enum FromStrError {
152 InvalidPrefix,
154 Hex(base16::DecodeError),
156 Account(TryFromSliceForAccountHashError),
158 Hash(TryFromSliceError),
160 AccountHash(account::FromStrError),
162 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
211pub type ContractVersion = u32;
213
214pub const CONTRACT_INITIAL_VERSION: ContractVersion = 1;
216
217pub type ProtocolVersionMajor = u32;
219
220#[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 pub fn new(
229 protocol_version_major: ProtocolVersionMajor,
230 contract_version: ContractVersion,
231 ) -> Self {
232 Self(protocol_version_major, contract_version)
233 }
234
235 pub fn protocol_version_major(self) -> ProtocolVersionMajor {
237 self.0
238 }
239
240 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
252pub 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
289pub type ContractVersions = BTreeMap<ContractVersionKey, ContractHash>;
291
292pub type DisabledVersions = BTreeSet<ContractVersionKey>;
295
296#[derive(Default, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
298#[cfg_attr(feature = "datasize", derive(DataSize))]
299pub struct ContractHash(HashAddr);
300
301impl ContractHash {
302 pub const fn new(value: HashAddr) -> ContractHash {
304 ContractHash(value)
305 }
306
307 pub fn value(&self) -> HashAddr {
309 self.0
310 }
311
312 pub fn as_bytes(&self) -> &[u8] {
314 &self.0
315 }
316
317 pub fn to_formatted_string(self) -> String {
319 format!(
320 "{}{}",
321 CONTRACT_STRING_PREFIX,
322 base16::encode_lower(&self.0),
323 )
324 }
325
326 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#[derive(Default, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
456#[cfg_attr(feature = "datasize", derive(DataSize))]
457pub struct ContractPackageHash(HashAddr);
458
459impl ContractPackageHash {
460 pub const fn new(value: HashAddr) -> ContractPackageHash {
462 ContractPackageHash(value)
463 }
464
465 pub fn value(&self) -> HashAddr {
467 self.0
468 }
469
470 pub fn as_bytes(&self) -> &[u8] {
472 &self.0
473 }
474
475 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 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#[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 Locked,
625 Unlocked,
627}
628
629impl ContractPackageStatus {
630 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#[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 access_key: URef,
687 #[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: DisabledVersions,
697 groups: Groups,
702 lock_status: ContractPackageStatus,
704}
705
706impl CLTyped for ContractPackage {
707 fn cl_type() -> CLType {
708 CLType::Any
709 }
710}
711
712impl ContractPackage {
713 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 pub fn access_key(&self) -> URef {
732 self.access_key
733 }
734
735 pub fn groups(&self) -> &Groups {
737 &self.groups
738 }
739
740 pub fn versions(&self) -> &ContractVersions {
742 &self.versions
743 }
744
745 pub fn versions_mut(&mut self) -> &mut ContractVersions {
747 &mut self.versions
748 }
749
750 pub fn take_versions(self) -> ContractVersions {
752 self.versions
753 }
754
755 pub fn take_groups(self) -> Groups {
757 self.groups
758 }
759
760 pub fn disabled_versions(&self) -> &DisabledVersions {
762 &self.disabled_versions
763 }
764
765 pub fn disabled_versions_mut(&mut self) -> &mut DisabledVersions {
767 &mut self.disabled_versions
768 }
769
770 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 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 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 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 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 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 pub fn current_contract_version(&self) -> Option<ContractVersionKey> {
859 self.enabled_versions().keys().next_back().copied()
860 }
861
862 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#[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 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 pub fn default_with_name<T: Into<String>>(name: T) -> Self {
1043 EntryPoint {
1044 name: name.into(),
1045 ..Default::default()
1046 }
1047 }
1048
1049 pub fn name(&self) -> &str {
1051 &self.name
1052 }
1053
1054 pub fn access(&self) -> &EntryPointAccess {
1056 &self.access
1057 }
1058
1059 pub fn args(&self) -> &[Parameter] {
1061 self.args.as_slice()
1062 }
1063
1064 pub fn ret(&self) -> &CLType {
1066 &self.ret
1067 }
1068
1069 pub fn entry_point_type(&self) -> EntryPointType {
1071 self.entry_point_type
1072 }
1073}
1074
1075impl Default for EntryPoint {
1076 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#[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 pub const fn new() -> EntryPoints {
1209 EntryPoints(BTreeMap::<String, EntryPoint>::new())
1210 }
1211
1212 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 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 pub fn has_entry_point(&self, entry_point_name: &str) -> bool {
1227 self.0.contains_key(entry_point_name)
1228 }
1229
1230 pub fn get(&self, entry_point_name: &str) -> Option<&EntryPoint> {
1232 self.0.get(entry_point_name)
1233 }
1234
1235 pub fn keys(&self) -> impl Iterator<Item = &String> {
1237 self.0.keys()
1238 }
1239
1240 pub fn take_entry_points(self) -> Vec<EntryPoint> {
1242 self.0.into_values().collect()
1243 }
1244
1245 pub fn len(&self) -> usize {
1247 self.0.len()
1248 }
1249
1250 pub fn is_empty(&self) -> bool {
1252 self.0.is_empty()
1253 }
1254
1255 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#[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 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 pub fn contract_package_hash(&self) -> ContractPackageHash {
1316 self.contract_package_hash
1317 }
1318
1319 pub fn contract_wasm_hash(&self) -> ContractWasmHash {
1321 self.contract_wasm_hash
1322 }
1323
1324 pub fn has_entry_point(&self, name: &str) -> bool {
1326 self.entry_points.has_entry_point(name)
1327 }
1328
1329 pub fn entry_point(&self, method: &str) -> Option<&EntryPoint> {
1331 self.entry_points.get(method)
1332 }
1333
1334 pub fn protocol_version(&self) -> ProtocolVersion {
1336 self.protocol_version
1337 }
1338
1339 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 pub fn contract_wasm_key(&self) -> Key {
1346 self.contract_wasm_hash.into()
1347 }
1348
1349 pub fn entry_points(&self) -> &EntryPoints {
1351 &self.entry_points
1352 }
1353
1354 pub fn take_named_keys(self) -> NamedKeys {
1356 self.named_keys
1357 }
1358
1359 pub fn named_keys(&self) -> &NamedKeys {
1361 &self.named_keys
1362 }
1363
1364 pub fn named_keys_append(&mut self, keys: NamedKeys) {
1366 self.named_keys.append(keys);
1367 }
1368
1369 pub fn remove_named_key(&mut self, key: &str) -> Option<Key> {
1371 self.named_keys.remove(key)
1372 }
1373
1374 pub fn set_protocol_version(&mut self, protocol_version: ProtocolVersion) {
1376 self.protocol_version = protocol_version;
1377 }
1378
1379 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
1446pub const DEFAULT_ENTRY_POINT_NAME: &str = "call";
1448
1449pub const ENTRY_POINT_NAME_INSTALL: &str = "install";
1451
1452pub 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 {
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 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}