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