1#![allow(clippy::field_reassign_with_default)]
4
5pub mod action_thresholds;
6mod action_type;
7pub mod associated_keys;
8mod entry_points;
9mod error;
10mod weight;
12
13use alloc::{
14 collections::{btree_map::Entry, BTreeMap, BTreeSet},
15 format,
16 string::{String, ToString},
17 vec::Vec,
18};
19use blake2::{
20 digest::{Update, VariableOutput},
21 VarBlake2b,
22};
23use core::{
24 array::TryFromSliceError,
25 convert::{TryFrom, TryInto},
26 fmt::{self, Debug, Display, Formatter},
27 iter,
28};
29
30#[cfg(feature = "json-schema")]
31use crate::SecretKey;
32#[cfg(feature = "datasize")]
33use datasize::DataSize;
34#[cfg(feature = "json-schema")]
35use once_cell::sync::Lazy;
36#[cfg(any(feature = "testing", test))]
37use rand::{
38 distributions::{Distribution, Standard},
39 Rng,
40};
41#[cfg(feature = "json-schema")]
42use schemars::JsonSchema;
43use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
44#[cfg(feature = "json-schema")]
45use serde_map_to_array::KeyValueJsonSchema;
46use serde_map_to_array::{BTreeMapToArray, KeyValueLabels};
47
48pub use self::{
49 action_thresholds::ActionThresholds,
50 action_type::ActionType,
51 associated_keys::AssociatedKeys,
52 entry_points::{
53 EntityEntryPoint, EntryPointAccess, EntryPointAddr, EntryPointPayment, EntryPointType,
54 EntryPointValue, EntryPoints, Parameter, Parameters, DEFAULT_ENTRY_POINT_NAME,
55 },
56 error::{FromAccountHashStrError, TryFromIntError, TryFromSliceForAccountHashError},
57 weight::{Weight, WEIGHT_SERIALIZED_LENGTH},
58};
59use crate::{
60 account::{
61 Account, AccountHash, AddKeyFailure, RemoveKeyFailure, SetThresholdFailure,
62 UpdateKeyFailure,
63 },
64 byte_code::ByteCodeHash,
65 bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
66 checksummed_hex,
67 contract_messages::TopicNameHash,
68 contracts::{Contract, ContractHash},
69 system::SystemEntityType,
70 uref::{self, URef},
71 AccessRights, ApiError, CLType, CLTyped, CLValue, CLValueError, ContextAccessRights, HashAddr,
72 Key, NamedKeys, PackageHash, ProtocolVersion, PublicKey, Tagged, BLAKE2B_DIGEST_LENGTH,
73 KEY_HASH_LENGTH,
74};
75
76pub const MAX_GROUPS: u8 = 10;
78pub const MAX_TOTAL_UREFS: usize = 100;
80
81pub const ADDRESSABLE_ENTITY_STRING_PREFIX: &str = "addressable-entity-";
84pub const ENTITY_PREFIX: &str = "entity-";
87pub const ACCOUNT_ENTITY_PREFIX: &str = "account-";
90pub const CONTRACT_ENTITY_PREFIX: &str = "contract-";
93pub const SYSTEM_ENTITY_PREFIX: &str = "system-";
96pub const NAMED_KEY_PREFIX: &str = "named-key-";
99
100#[derive(Debug, PartialEq, Eq)]
102#[repr(u8)]
103#[non_exhaustive]
104pub enum Error {
105 PreviouslyUsedVersion = 1,
113 EntityNotFound = 2,
119 GroupAlreadyExists = 3,
126 MaxGroupsExceeded = 4,
133 MaxTotalURefsExceeded = 5,
140 GroupDoesNotExist = 6,
147 UnableToRemoveURef = 7,
153 GroupInUse = 8,
159 URefAlreadyExists = 9,
165}
166
167impl TryFrom<u8> for Error {
168 type Error = ();
169
170 fn try_from(value: u8) -> Result<Self, Self::Error> {
171 let error = match value {
172 v if v == Self::PreviouslyUsedVersion as u8 => Self::PreviouslyUsedVersion,
173 v if v == Self::EntityNotFound as u8 => Self::EntityNotFound,
174 v if v == Self::GroupAlreadyExists as u8 => Self::GroupAlreadyExists,
175 v if v == Self::MaxGroupsExceeded as u8 => Self::MaxGroupsExceeded,
176 v if v == Self::MaxTotalURefsExceeded as u8 => Self::MaxTotalURefsExceeded,
177 v if v == Self::GroupDoesNotExist as u8 => Self::GroupDoesNotExist,
178 v if v == Self::UnableToRemoveURef as u8 => Self::UnableToRemoveURef,
179 v if v == Self::GroupInUse as u8 => Self::GroupInUse,
180 v if v == Self::URefAlreadyExists as u8 => Self::URefAlreadyExists,
181 _ => return Err(()),
182 };
183 Ok(error)
184 }
185}
186
187#[derive(Debug)]
189pub struct TryFromSliceForContractHashError(());
190
191impl Display for TryFromSliceForContractHashError {
192 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
193 write!(f, "failed to retrieve from slice")
194 }
195}
196
197#[derive(Debug)]
199#[non_exhaustive]
200pub enum FromStrError {
201 InvalidPrefix,
203 Hex(base16::DecodeError),
205 Account(TryFromSliceForAccountHashError),
207 Hash(TryFromSliceError),
209 URef(uref::FromStrError),
211 BytesRepr(bytesrepr::Error),
213}
214
215impl From<base16::DecodeError> for FromStrError {
216 fn from(error: base16::DecodeError) -> Self {
217 FromStrError::Hex(error)
218 }
219}
220
221impl From<TryFromSliceError> for FromStrError {
222 fn from(error: TryFromSliceError) -> Self {
223 FromStrError::Hash(error)
224 }
225}
226
227impl From<uref::FromStrError> for FromStrError {
228 fn from(error: uref::FromStrError) -> Self {
229 FromStrError::URef(error)
230 }
231}
232
233impl Display for FromStrError {
234 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
235 match self {
236 FromStrError::InvalidPrefix => write!(f, "invalid prefix"),
237 FromStrError::Hex(error) => write!(f, "decode from hex: {}", error),
238 FromStrError::Hash(error) => write!(f, "hash from string error: {}", error),
239 FromStrError::URef(error) => write!(f, "uref from string error: {:?}", error),
240 FromStrError::Account(error) => {
241 write!(f, "account hash from string error: {:?}", error)
242 }
243 FromStrError::BytesRepr(error) => {
244 write!(f, "bytesrepr error: {:?}", error)
245 }
246 }
247 }
248}
249
250#[derive(Default, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
252#[cfg_attr(feature = "datasize", derive(DataSize))]
253#[cfg_attr(
254 feature = "json-schema",
255 derive(JsonSchema),
256 schemars(description = "The hex-encoded address of the addressable entity.")
257)]
258pub struct AddressableEntityHash(
259 #[cfg_attr(feature = "json-schema", schemars(skip, with = "String"))] HashAddr,
260);
261
262impl AddressableEntityHash {
263 pub const fn new(value: HashAddr) -> AddressableEntityHash {
265 AddressableEntityHash(value)
266 }
267
268 pub fn entity_addr(&self, entity: AddressableEntity) -> EntityAddr {
270 entity.entity_addr(*self)
271 }
272
273 pub fn value(&self) -> HashAddr {
275 self.0
276 }
277
278 pub fn as_bytes(&self) -> &[u8] {
280 &self.0
281 }
282
283 pub fn to_formatted_string(self) -> String {
285 format!(
286 "{}{}",
287 ADDRESSABLE_ENTITY_STRING_PREFIX,
288 base16::encode_lower(&self.0),
289 )
290 }
291
292 pub fn to_hex_string(&self) -> String {
294 base16::encode_lower(&self.0)
295 }
296
297 pub fn from_formatted_str(input: &str) -> Result<Self, FromStrError> {
300 let remainder = input
301 .strip_prefix(ADDRESSABLE_ENTITY_STRING_PREFIX)
302 .ok_or(FromStrError::InvalidPrefix)?;
303 let bytes = HashAddr::try_from(checksummed_hex::decode(remainder)?.as_ref())?;
304 Ok(AddressableEntityHash(bytes))
305 }
306}
307
308impl From<ContractHash> for AddressableEntityHash {
309 fn from(contract_hash: ContractHash) -> Self {
310 AddressableEntityHash::new(contract_hash.value())
311 }
312}
313
314impl Display for AddressableEntityHash {
315 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
316 write!(f, "{}", base16::encode_lower(&self.0))
317 }
318}
319
320impl Debug for AddressableEntityHash {
321 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
322 write!(
323 f,
324 "AddressableEntityHash({})",
325 base16::encode_lower(&self.0)
326 )
327 }
328}
329
330impl CLTyped for AddressableEntityHash {
331 fn cl_type() -> CLType {
332 CLType::ByteArray(KEY_HASH_LENGTH as u32)
333 }
334}
335
336impl ToBytes for AddressableEntityHash {
337 #[inline(always)]
338 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
339 self.0.to_bytes()
340 }
341
342 #[inline(always)]
343 fn serialized_length(&self) -> usize {
344 self.0.serialized_length()
345 }
346
347 #[inline(always)]
348 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
349 writer.extend_from_slice(&self.0);
350 Ok(())
351 }
352}
353
354impl FromBytes for AddressableEntityHash {
355 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
356 let (bytes, rem) = FromBytes::from_bytes(bytes)?;
357 Ok((AddressableEntityHash::new(bytes), rem))
358 }
359}
360
361impl From<[u8; 32]> for AddressableEntityHash {
362 fn from(bytes: [u8; 32]) -> Self {
363 AddressableEntityHash(bytes)
364 }
365}
366
367impl TryFrom<Key> for AddressableEntityHash {
368 type Error = ApiError;
369
370 fn try_from(value: Key) -> Result<Self, Self::Error> {
371 if let Key::AddressableEntity(entity_addr) = value {
372 Ok(AddressableEntityHash::new(entity_addr.value()))
373 } else {
374 Err(ApiError::Formatting)
375 }
376 }
377}
378
379impl Serialize for AddressableEntityHash {
380 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
381 if serializer.is_human_readable() {
382 self.to_formatted_string().serialize(serializer)
383 } else {
384 self.0.serialize(serializer)
385 }
386 }
387}
388
389impl<'de> Deserialize<'de> for AddressableEntityHash {
390 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
391 if deserializer.is_human_readable() {
392 let formatted_string = String::deserialize(deserializer)?;
393 AddressableEntityHash::from_formatted_str(&formatted_string).map_err(SerdeError::custom)
394 } else {
395 let bytes = HashAddr::deserialize(deserializer)?;
396 Ok(AddressableEntityHash(bytes))
397 }
398 }
399}
400
401impl AsRef<[u8]> for AddressableEntityHash {
402 fn as_ref(&self) -> &[u8] {
403 self.0.as_ref()
404 }
405}
406
407impl TryFrom<&[u8]> for AddressableEntityHash {
408 type Error = TryFromSliceForContractHashError;
409
410 fn try_from(bytes: &[u8]) -> Result<Self, TryFromSliceForContractHashError> {
411 HashAddr::try_from(bytes)
412 .map(AddressableEntityHash::new)
413 .map_err(|_| TryFromSliceForContractHashError(()))
414 }
415}
416
417impl TryFrom<&Vec<u8>> for AddressableEntityHash {
418 type Error = TryFromSliceForContractHashError;
419
420 fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
421 HashAddr::try_from(bytes as &[u8])
422 .map(AddressableEntityHash::new)
423 .map_err(|_| TryFromSliceForContractHashError(()))
424 }
425}
426
427#[cfg(any(feature = "testing", test))]
428impl Distribution<AddressableEntityHash> for Standard {
429 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> AddressableEntityHash {
430 AddressableEntityHash(rng.gen())
431 }
432}
433
434#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)]
436#[cfg_attr(feature = "datasize", derive(DataSize))]
437#[repr(u8)]
438pub enum EntityKindTag {
439 System = 0,
441 Account = 1,
443 SmartContract = 2,
445}
446
447impl TryFrom<u8> for EntityKindTag {
448 type Error = bytesrepr::Error;
449
450 fn try_from(value: u8) -> Result<Self, Self::Error> {
451 match value {
452 0 => Ok(EntityKindTag::System),
453 1 => Ok(EntityKindTag::Account),
454 2 => Ok(EntityKindTag::SmartContract),
455 _ => Err(bytesrepr::Error::Formatting),
456 }
457 }
458}
459
460impl ToBytes for EntityKindTag {
461 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
462 (*self as u8).to_bytes()
463 }
464
465 fn serialized_length(&self) -> usize {
466 U8_SERIALIZED_LENGTH
467 }
468
469 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
470 (*self as u8).write_bytes(writer)
471 }
472}
473
474impl FromBytes for EntityKindTag {
475 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
476 let (entity_kind_tag, remainder) = u8::from_bytes(bytes)?;
477 Ok((entity_kind_tag.try_into()?, remainder))
478 }
479}
480
481impl Display for EntityKindTag {
482 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
483 match self {
484 EntityKindTag::System => {
485 write!(f, "system")
486 }
487 EntityKindTag::Account => {
488 write!(f, "account")
489 }
490 EntityKindTag::SmartContract => {
491 write!(f, "contract")
492 }
493 }
494 }
495}
496
497#[cfg(any(feature = "testing", test))]
498impl Distribution<EntityKindTag> for Standard {
499 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> EntityKindTag {
500 match rng.gen_range(0..=2) {
501 0 => EntityKindTag::System,
502 1 => EntityKindTag::Account,
503 2 => EntityKindTag::SmartContract,
504 _ => unreachable!(),
505 }
506 }
507}
508
509#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)]
510#[cfg_attr(feature = "datasize", derive(DataSize))]
511#[cfg_attr(
512 feature = "json-schema",
513 derive(JsonSchema),
514 schemars(description = "Runtime used to execute a Transaction.")
515)]
516#[cfg_attr(any(feature = "testing", test), derive(Default))]
518#[serde(deny_unknown_fields)]
519#[repr(u8)]
520pub enum ContractRuntimeTag {
521 #[cfg_attr(any(feature = "testing", test), default)]
522 VmCasperV1,
523 VmCasperV2,
524}
525
526#[cfg(any(feature = "testing", test))]
527impl Distribution<ContractRuntimeTag> for Standard {
528 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> ContractRuntimeTag {
529 match rng.gen_range(0..=1) {
530 0 => ContractRuntimeTag::VmCasperV1,
531 1 => ContractRuntimeTag::VmCasperV2,
532 _ => unreachable!(),
533 }
534 }
535}
536
537impl ToBytes for ContractRuntimeTag {
538 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
539 (*self as u8).to_bytes()
540 }
541
542 fn serialized_length(&self) -> usize {
543 U8_SERIALIZED_LENGTH
544 }
545
546 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
547 (*self as u8).write_bytes(writer)
548 }
549}
550
551impl FromBytes for ContractRuntimeTag {
552 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
553 let (tag, remainder) = u8::from_bytes(bytes)?;
554 if tag == ContractRuntimeTag::VmCasperV1 as u8 {
555 Ok((ContractRuntimeTag::VmCasperV1, remainder))
556 } else if tag == ContractRuntimeTag::VmCasperV2 as u8 {
557 Ok((ContractRuntimeTag::VmCasperV2, remainder))
558 } else {
559 Err(bytesrepr::Error::Formatting)
560 }
561 }
562}
563
564impl Display for ContractRuntimeTag {
565 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
566 match self {
567 ContractRuntimeTag::VmCasperV1 => write!(f, "vm-casper-v1"),
568 ContractRuntimeTag::VmCasperV2 => write!(f, "vm-casper-v2"),
569 }
570 }
571}
572impl ContractRuntimeTag {
573 pub fn tag(&self) -> u8 {
575 *self as u8
576 }
577}
578
579#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
580#[cfg_attr(feature = "datasize", derive(DataSize))]
581#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
582pub enum EntityKind {
584 System(SystemEntityType),
586 Account(AccountHash),
588 SmartContract(ContractRuntimeTag),
590}
591
592impl EntityKind {
593 pub fn maybe_account_hash(&self) -> Option<AccountHash> {
595 match self {
596 Self::Account(account_hash) => Some(*account_hash),
597 Self::SmartContract(_) | Self::System(_) => None,
598 }
599 }
600
601 pub fn associated_keys(&self) -> AssociatedKeys {
603 match self {
604 Self::Account(account_hash) => AssociatedKeys::new(*account_hash, Weight::new(1)),
605 Self::SmartContract(_) | Self::System(_) => AssociatedKeys::default(),
606 }
607 }
608
609 pub fn is_system(&self) -> bool {
611 matches!(self, Self::System(_))
612 }
613
614 pub fn is_system_mint(&self) -> bool {
616 matches!(self, Self::System(SystemEntityType::Mint))
617 }
618
619 pub fn is_system_auction(&self) -> bool {
621 matches!(self, Self::System(SystemEntityType::Auction))
622 }
623
624 pub fn is_system_account(&self) -> bool {
626 match self {
627 Self::Account(account_hash) => {
628 if *account_hash == PublicKey::System.to_account_hash() {
629 return true;
630 }
631 false
632 }
633 _ => false,
634 }
635 }
636}
637
638impl Tagged<EntityKindTag> for EntityKind {
639 fn tag(&self) -> EntityKindTag {
640 match self {
641 EntityKind::System(_) => EntityKindTag::System,
642 EntityKind::Account(_) => EntityKindTag::Account,
643 EntityKind::SmartContract(_) => EntityKindTag::SmartContract,
644 }
645 }
646}
647
648impl Tagged<u8> for EntityKind {
649 fn tag(&self) -> u8 {
650 let package_kind_tag: EntityKindTag = self.tag();
651 package_kind_tag as u8
652 }
653}
654
655impl ToBytes for EntityKind {
656 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
657 let mut buffer = bytesrepr::allocate_buffer(self)?;
658 self.write_bytes(&mut buffer)?;
659 Ok(buffer)
660 }
661
662 fn serialized_length(&self) -> usize {
663 U8_SERIALIZED_LENGTH
664 + match self {
665 EntityKind::SmartContract(transaction_runtime) => {
666 transaction_runtime.serialized_length()
667 }
668 EntityKind::System(system_entity_type) => system_entity_type.serialized_length(),
669 EntityKind::Account(account_hash) => account_hash.serialized_length(),
670 }
671 }
672
673 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
674 match self {
675 EntityKind::SmartContract(transaction_runtime) => {
676 writer.push(self.tag());
677 transaction_runtime.write_bytes(writer)
678 }
679 EntityKind::System(system_entity_type) => {
680 writer.push(self.tag());
681 system_entity_type.write_bytes(writer)
682 }
683 EntityKind::Account(account_hash) => {
684 writer.push(self.tag());
685 account_hash.write_bytes(writer)
686 }
687 }
688 }
689}
690
691impl FromBytes for EntityKind {
692 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
693 let (tag, remainder) = EntityKindTag::from_bytes(bytes)?;
694 match tag {
695 EntityKindTag::System => {
696 let (entity_type, remainder) = SystemEntityType::from_bytes(remainder)?;
697 Ok((EntityKind::System(entity_type), remainder))
698 }
699 EntityKindTag::Account => {
700 let (account_hash, remainder) = AccountHash::from_bytes(remainder)?;
701 Ok((EntityKind::Account(account_hash), remainder))
702 }
703 EntityKindTag::SmartContract => {
704 let (transaction_runtime, remainder) = FromBytes::from_bytes(remainder)?;
705 Ok((EntityKind::SmartContract(transaction_runtime), remainder))
706 }
707 }
708 }
709}
710
711impl Display for EntityKind {
712 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
713 match self {
714 EntityKind::System(system_entity) => {
715 write!(f, "system-entity-kind({})", system_entity)
716 }
717 EntityKind::Account(account_hash) => {
718 write!(f, "account-entity-kind({})", account_hash)
719 }
720 EntityKind::SmartContract(transaction_runtime) => {
721 write!(f, "smart-contract-entity-kind({})", transaction_runtime)
722 }
723 }
724 }
725}
726
727#[cfg(any(feature = "testing", test))]
728impl Distribution<EntityKind> for Standard {
729 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> EntityKind {
730 match rng.gen_range(0..=2) {
731 0 => EntityKind::System(rng.gen()),
732 1 => EntityKind::Account(rng.gen()),
733 2 => EntityKind::SmartContract(rng.gen()),
734 _ => unreachable!(),
735 }
736 }
737}
738
739#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
741#[cfg_attr(feature = "datasize", derive(DataSize))]
742#[cfg_attr(feature = "json-schema", derive(JsonSchema), schemars(untagged))]
743pub enum EntityAddr {
744 System(#[cfg_attr(feature = "json-schema", schemars(skip, with = "String"))] HashAddr),
746 Account(#[cfg_attr(feature = "json-schema", schemars(skip, with = "String"))] HashAddr),
748 SmartContract(#[cfg_attr(feature = "json-schema", schemars(skip, with = "String"))] HashAddr),
750}
751
752impl EntityAddr {
753 pub const LENGTH: usize = U8_SERIALIZED_LENGTH + KEY_HASH_LENGTH;
755
756 pub const fn new_system(hash_addr: HashAddr) -> Self {
758 Self::System(hash_addr)
759 }
760
761 pub const fn new_account(hash_addr: HashAddr) -> Self {
763 Self::Account(hash_addr)
764 }
765
766 pub const fn new_smart_contract(hash_addr: HashAddr) -> Self {
768 Self::SmartContract(hash_addr)
769 }
770
771 pub fn new_of_kind(entity_kind: EntityKind, hash_addr: HashAddr) -> Self {
773 match entity_kind {
774 EntityKind::System(_) => Self::new_system(hash_addr),
775 EntityKind::Account(_) => Self::new_account(hash_addr),
776 EntityKind::SmartContract(_) => Self::new_smart_contract(hash_addr),
777 }
778 }
779
780 pub fn tag(&self) -> EntityKindTag {
782 match self {
783 EntityAddr::System(_) => EntityKindTag::System,
784 EntityAddr::Account(_) => EntityKindTag::Account,
785 EntityAddr::SmartContract(_) => EntityKindTag::SmartContract,
786 }
787 }
788
789 pub fn is_system(&self) -> bool {
791 self.tag() == EntityKindTag::System
792 || self.value() == PublicKey::System.to_account_hash().value()
793 }
794
795 pub fn is_contract(&self) -> bool {
797 self.tag() == EntityKindTag::SmartContract
798 }
799
800 pub fn is_account(&self) -> bool {
802 self.tag() == EntityKindTag::Account
803 }
804
805 pub fn value(&self) -> HashAddr {
807 match self {
808 EntityAddr::System(hash_addr)
809 | EntityAddr::Account(hash_addr)
810 | EntityAddr::SmartContract(hash_addr) => *hash_addr,
811 }
812 }
813
814 pub fn to_formatted_string(&self) -> String {
816 match self {
817 EntityAddr::System(addr) => {
818 format!(
819 "{}{}{}",
820 ENTITY_PREFIX,
821 SYSTEM_ENTITY_PREFIX,
822 base16::encode_lower(addr)
823 )
824 }
825 EntityAddr::Account(addr) => {
826 format!(
827 "{}{}{}",
828 ENTITY_PREFIX,
829 ACCOUNT_ENTITY_PREFIX,
830 base16::encode_lower(addr)
831 )
832 }
833 EntityAddr::SmartContract(addr) => {
834 format!(
835 "{}{}{}",
836 ENTITY_PREFIX,
837 CONTRACT_ENTITY_PREFIX,
838 base16::encode_lower(addr)
839 )
840 }
841 }
842 }
843
844 pub fn from_formatted_str(input: &str) -> Result<Self, FromStrError> {
846 if let Some(entity) = input.strip_prefix(ENTITY_PREFIX) {
847 let (addr_str, tag) = if let Some(str) = entity.strip_prefix(SYSTEM_ENTITY_PREFIX) {
848 (str, EntityKindTag::System)
849 } else if let Some(str) = entity.strip_prefix(ACCOUNT_ENTITY_PREFIX) {
850 (str, EntityKindTag::Account)
851 } else if let Some(str) = entity.strip_prefix(CONTRACT_ENTITY_PREFIX) {
852 (str, EntityKindTag::SmartContract)
853 } else {
854 return Err(FromStrError::InvalidPrefix);
855 };
856 let addr = checksummed_hex::decode(addr_str).map_err(FromStrError::Hex)?;
857 let hash_addr = HashAddr::try_from(addr.as_ref()).map_err(FromStrError::Hash)?;
858 let entity_addr = match tag {
859 EntityKindTag::System => EntityAddr::new_system(hash_addr),
860 EntityKindTag::Account => EntityAddr::new_account(hash_addr),
861 EntityKindTag::SmartContract => EntityAddr::new_smart_contract(hash_addr),
862 };
863
864 return Ok(entity_addr);
865 }
866
867 Err(FromStrError::InvalidPrefix)
868 }
869
870 pub fn into_smart_contract(&self) -> Option<[u8; 32]> {
871 match self {
872 EntityAddr::SmartContract(addr) => Some(*addr),
873 _ => None,
874 }
875 }
876}
877
878impl ToBytes for EntityAddr {
879 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
880 let mut buffer = bytesrepr::allocate_buffer(self)?;
881 self.write_bytes(&mut buffer)?;
882 Ok(buffer)
883 }
884
885 fn serialized_length(&self) -> usize {
886 EntityAddr::LENGTH
887 }
888
889 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
890 match self {
891 EntityAddr::System(addr) => {
892 EntityKindTag::System.write_bytes(writer)?;
893 addr.write_bytes(writer)
894 }
895 EntityAddr::Account(addr) => {
896 EntityKindTag::Account.write_bytes(writer)?;
897 addr.write_bytes(writer)
898 }
899 EntityAddr::SmartContract(addr) => {
900 EntityKindTag::SmartContract.write_bytes(writer)?;
901 addr.write_bytes(writer)
902 }
903 }
904 }
905}
906
907impl FromBytes for EntityAddr {
908 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
909 let (tag, remainder) = EntityKindTag::from_bytes(bytes)?;
910 let (addr, remainder) = HashAddr::from_bytes(remainder)?;
911 let entity_addr = match tag {
912 EntityKindTag::System => EntityAddr::System(addr),
913 EntityKindTag::Account => EntityAddr::Account(addr),
914 EntityKindTag::SmartContract => EntityAddr::SmartContract(addr),
915 };
916 Ok((entity_addr, remainder))
917 }
918}
919
920impl CLTyped for EntityAddr {
921 fn cl_type() -> CLType {
922 CLType::Any
923 }
924}
925
926impl From<EntityAddr> for AddressableEntityHash {
927 fn from(entity_addr: EntityAddr) -> Self {
928 AddressableEntityHash::new(entity_addr.value())
929 }
930}
931
932impl Display for EntityAddr {
933 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
934 f.write_str(&self.to_formatted_string())
935 }
936}
937
938impl Debug for EntityAddr {
939 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
940 match self {
941 EntityAddr::System(hash_addr) => {
942 write!(f, "EntityAddr::System({})", base16::encode_lower(hash_addr))
943 }
944 EntityAddr::Account(hash_addr) => {
945 write!(
946 f,
947 "EntityAddr::Account({})",
948 base16::encode_lower(hash_addr)
949 )
950 }
951 EntityAddr::SmartContract(hash_addr) => {
952 write!(
953 f,
954 "EntityAddr::SmartContract({})",
955 base16::encode_lower(hash_addr)
956 )
957 }
958 }
959 }
960}
961
962impl Serialize for EntityAddr {
963 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
964 if serializer.is_human_readable() {
965 self.to_formatted_string().serialize(serializer)
966 } else {
967 let (tag, value): (EntityKindTag, HashAddr) = (self.tag(), self.value());
968 (tag, value).serialize(serializer)
969 }
970 }
971}
972
973impl<'de> Deserialize<'de> for EntityAddr {
974 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
975 if deserializer.is_human_readable() {
976 let formatted_string = String::deserialize(deserializer)?;
977 Self::from_formatted_str(&formatted_string).map_err(SerdeError::custom)
978 } else {
979 let (tag, addr) = <(EntityKindTag, HashAddr)>::deserialize(deserializer)?;
980 match tag {
981 EntityKindTag::System => Ok(EntityAddr::new_system(addr)),
982 EntityKindTag::Account => Ok(EntityAddr::new_account(addr)),
983 EntityKindTag::SmartContract => Ok(EntityAddr::new_smart_contract(addr)),
984 }
985 }
986 }
987}
988
989#[cfg(any(feature = "testing", test))]
990impl Distribution<EntityAddr> for Standard {
991 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> EntityAddr {
992 match rng.gen_range(0..=2) {
993 0 => EntityAddr::System(rng.gen()),
994 1 => EntityAddr::Account(rng.gen()),
995 2 => EntityAddr::SmartContract(rng.gen()),
996 _ => unreachable!(),
997 }
998 }
999}
1000
1001#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
1003#[cfg_attr(feature = "datasize", derive(DataSize))]
1004#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
1005pub struct NamedKeyAddr {
1006 base_addr: EntityAddr,
1008 string_bytes: [u8; KEY_HASH_LENGTH],
1010}
1011
1012impl NamedKeyAddr {
1013 pub const NAMED_KEY_ADDR_BASE_LENGTH: usize = 1 + EntityAddr::LENGTH;
1015
1016 pub const fn new_named_key_entry(
1018 entity_addr: EntityAddr,
1019 string_bytes: [u8; KEY_HASH_LENGTH],
1020 ) -> Self {
1021 Self {
1022 base_addr: entity_addr,
1023 string_bytes,
1024 }
1025 }
1026
1027 pub fn new_from_string(
1030 entity_addr: EntityAddr,
1031 entry: String,
1032 ) -> Result<Self, bytesrepr::Error> {
1033 let bytes = entry.to_bytes()?;
1034 let mut hasher = {
1035 match VarBlake2b::new(BLAKE2B_DIGEST_LENGTH) {
1036 Ok(hasher) => hasher,
1037 Err(_) => return Err(bytesrepr::Error::Formatting),
1038 }
1039 };
1040 hasher.update(bytes);
1041 let mut string_bytes = HashAddr::default();
1043 hasher.finalize_variable(|hash| string_bytes.clone_from_slice(hash));
1044 Ok(Self::new_named_key_entry(entity_addr, string_bytes))
1045 }
1046
1047 pub fn entity_addr(&self) -> EntityAddr {
1049 self.base_addr
1050 }
1051
1052 pub fn to_formatted_string(&self) -> String {
1054 format!("{}", self)
1055 }
1056
1057 pub fn from_formatted_str(input: &str) -> Result<Self, FromStrError> {
1059 if let Some(named_key) = input.strip_prefix(NAMED_KEY_PREFIX) {
1060 if let Some((entity_addr_str, string_bytes_str)) = named_key.rsplit_once('-') {
1061 let entity_addr = EntityAddr::from_formatted_str(entity_addr_str)?;
1062 let string_bytes =
1063 checksummed_hex::decode(string_bytes_str).map_err(FromStrError::Hex)?;
1064 let (string_bytes, _) =
1065 FromBytes::from_vec(string_bytes).map_err(FromStrError::BytesRepr)?;
1066 return Ok(Self::new_named_key_entry(entity_addr, string_bytes));
1067 };
1068 }
1069
1070 Err(FromStrError::InvalidPrefix)
1071 }
1072}
1073
1074impl Default for NamedKeyAddr {
1075 fn default() -> Self {
1076 NamedKeyAddr {
1077 base_addr: EntityAddr::System(HashAddr::default()),
1078 string_bytes: Default::default(),
1079 }
1080 }
1081}
1082
1083impl ToBytes for NamedKeyAddr {
1084 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1085 let mut buffer = bytesrepr::allocate_buffer(self)?;
1086 buffer.append(&mut self.base_addr.to_bytes()?);
1087 buffer.append(&mut self.string_bytes.to_bytes()?);
1088 Ok(buffer)
1089 }
1090
1091 fn serialized_length(&self) -> usize {
1092 self.base_addr.serialized_length() + self.string_bytes.serialized_length()
1093 }
1094}
1095
1096impl FromBytes for NamedKeyAddr {
1097 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1098 let (base_addr, remainder) = EntityAddr::from_bytes(bytes)?;
1099 let (string_bytes, remainder) = FromBytes::from_bytes(remainder)?;
1100 Ok((
1101 Self {
1102 base_addr,
1103 string_bytes,
1104 },
1105 remainder,
1106 ))
1107 }
1108}
1109
1110impl Display for NamedKeyAddr {
1111 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1112 write!(
1113 f,
1114 "{}{}-{}",
1115 NAMED_KEY_PREFIX,
1116 self.base_addr,
1117 base16::encode_lower(&self.string_bytes)
1118 )
1119 }
1120}
1121
1122impl Debug for NamedKeyAddr {
1123 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1124 write!(
1125 f,
1126 "NamedKeyAddr({:?}-{:?})",
1127 self.base_addr,
1128 base16::encode_lower(&self.string_bytes)
1129 )
1130 }
1131}
1132
1133#[cfg(any(feature = "testing", test))]
1134impl Distribution<NamedKeyAddr> for Standard {
1135 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> NamedKeyAddr {
1136 NamedKeyAddr {
1137 base_addr: rng.gen(),
1138 string_bytes: rng.gen(),
1139 }
1140 }
1141}
1142
1143#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
1145#[cfg_attr(feature = "datasize", derive(DataSize))]
1146#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
1147pub struct NamedKeyValue {
1148 named_key: CLValue,
1150 name: CLValue,
1152}
1153
1154impl NamedKeyValue {
1155 pub fn new(key: CLValue, name: CLValue) -> Self {
1157 Self {
1158 named_key: key,
1159 name,
1160 }
1161 }
1162
1163 pub fn from_concrete_values(named_key: Key, name: String) -> Result<Self, CLValueError> {
1165 let key_cl_value = CLValue::from_t(named_key)?;
1166 let string_cl_value = CLValue::from_t(name)?;
1167 Ok(Self::new(key_cl_value, string_cl_value))
1168 }
1169
1170 pub fn get_key_as_cl_value(&self) -> &CLValue {
1172 &self.named_key
1173 }
1174
1175 pub fn get_name_as_cl_value(&self) -> &CLValue {
1177 &self.name
1178 }
1179
1180 pub fn get_key(&self) -> Result<Key, CLValueError> {
1182 self.named_key.clone().into_t::<Key>()
1183 }
1184
1185 pub fn get_name(&self) -> Result<String, CLValueError> {
1187 self.name.clone().into_t::<String>()
1188 }
1189}
1190
1191impl ToBytes for NamedKeyValue {
1192 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1193 let mut buffer = bytesrepr::allocate_buffer(self)?;
1194 buffer.append(&mut self.named_key.to_bytes()?);
1195 buffer.append(&mut self.name.to_bytes()?);
1196 Ok(buffer)
1197 }
1198
1199 fn serialized_length(&self) -> usize {
1200 self.named_key.serialized_length() + self.name.serialized_length()
1201 }
1202}
1203
1204impl FromBytes for NamedKeyValue {
1205 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1206 let (named_key, remainder) = CLValue::from_bytes(bytes)?;
1207 let (name, remainder) = CLValue::from_bytes(remainder)?;
1208 Ok((Self { named_key, name }, remainder))
1209 }
1210}
1211
1212#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Default)]
1214#[cfg_attr(feature = "datasize", derive(DataSize))]
1215#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
1216#[serde(transparent, deny_unknown_fields)]
1217pub struct MessageTopics(
1218 #[serde(with = "BTreeMapToArray::<String, TopicNameHash, MessageTopicLabels>")]
1219 BTreeMap<String, TopicNameHash>,
1220);
1221
1222impl ToBytes for MessageTopics {
1223 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1224 self.0.to_bytes()
1225 }
1226
1227 fn serialized_length(&self) -> usize {
1228 self.0.serialized_length()
1229 }
1230
1231 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
1232 self.0.write_bytes(writer)
1233 }
1234}
1235
1236impl FromBytes for MessageTopics {
1237 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1238 let (message_topics_map, remainder) = BTreeMap::<String, TopicNameHash>::from_bytes(bytes)?;
1239 Ok((MessageTopics(message_topics_map), remainder))
1240 }
1241}
1242
1243impl MessageTopics {
1244 pub fn add_topic(
1246 &mut self,
1247 topic_name: &str,
1248 topic_name_hash: TopicNameHash,
1249 ) -> Result<(), MessageTopicError> {
1250 match self.0.entry(topic_name.to_string()) {
1251 Entry::Vacant(entry) => {
1252 entry.insert(topic_name_hash);
1253 Ok(())
1254 }
1255 Entry::Occupied(_) => Err(MessageTopicError::DuplicateTopic),
1256 }
1257 }
1258
1259 pub fn has_topic(&self, topic_name: &str) -> bool {
1261 self.0.contains_key(topic_name)
1262 }
1263
1264 pub fn get(&self, topic_name: &str) -> Option<&TopicNameHash> {
1266 self.0.get(topic_name)
1267 }
1268
1269 pub fn len(&self) -> usize {
1271 self.0.len()
1272 }
1273
1274 pub fn is_empty(&self) -> bool {
1276 self.0.is_empty()
1277 }
1278
1279 pub fn iter(&self) -> impl Iterator<Item = (&String, &TopicNameHash)> {
1281 self.0.iter()
1282 }
1283}
1284
1285struct MessageTopicLabels;
1286
1287impl KeyValueLabels for MessageTopicLabels {
1288 const KEY: &'static str = "topic_name";
1289 const VALUE: &'static str = "topic_name_hash";
1290}
1291
1292#[cfg(feature = "json-schema")]
1293impl KeyValueJsonSchema for MessageTopicLabels {
1294 const JSON_SCHEMA_KV_NAME: Option<&'static str> = Some("MessageTopic");
1295}
1296
1297impl From<BTreeMap<String, TopicNameHash>> for MessageTopics {
1298 fn from(topics: BTreeMap<String, TopicNameHash>) -> MessageTopics {
1299 MessageTopics(topics)
1300 }
1301}
1302
1303#[derive(PartialEq, Eq, Debug, Clone)]
1305#[non_exhaustive]
1306pub enum MessageTopicError {
1307 DuplicateTopic,
1309 MaxTopicsExceeded,
1311 TopicNameSizeExceeded,
1313}
1314
1315#[cfg(feature = "json-schema")]
1316static ADDRESSABLE_ENTITY: Lazy<AddressableEntity> = Lazy::new(|| {
1317 let secret_key = SecretKey::ed25519_from_bytes([0; 32]).unwrap();
1318 let account_hash = PublicKey::from(&secret_key).to_account_hash();
1319 let package_hash = PackageHash::new([0; 32]);
1320 let byte_code_hash = ByteCodeHash::new([0; 32]);
1321 let main_purse = URef::from_formatted_str(
1322 "uref-09480c3248ef76b603d386f3f4f8a5f87f597d4eaffd475433f861af187ab5db-007",
1323 )
1324 .unwrap();
1325 let weight = Weight::new(1);
1326 let associated_keys = AssociatedKeys::new(account_hash, weight);
1327 let action_thresholds = ActionThresholds::new(weight, weight, weight).unwrap();
1328 let protocol_version = ProtocolVersion::from_parts(2, 0, 0);
1329 AddressableEntity {
1330 protocol_version,
1331 entity_kind: EntityKind::Account(account_hash),
1332 package_hash,
1333 byte_code_hash,
1334 main_purse,
1335 associated_keys,
1336 action_thresholds,
1337 }
1338});
1339
1340pub type ContractAddress = PackageHash;
1342
1343#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1345#[cfg_attr(feature = "datasize", derive(DataSize))]
1346#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
1347pub struct AddressableEntity {
1348 protocol_version: ProtocolVersion,
1349 entity_kind: EntityKind,
1350 package_hash: PackageHash,
1351 byte_code_hash: ByteCodeHash,
1352 main_purse: URef,
1353
1354 associated_keys: AssociatedKeys,
1355 action_thresholds: ActionThresholds,
1356}
1357
1358impl From<AddressableEntity>
1359 for (
1360 PackageHash,
1361 ByteCodeHash,
1362 ProtocolVersion,
1363 URef,
1364 AssociatedKeys,
1365 ActionThresholds,
1366 )
1367{
1368 fn from(entity: AddressableEntity) -> Self {
1369 (
1370 entity.package_hash,
1371 entity.byte_code_hash,
1372 entity.protocol_version,
1373 entity.main_purse,
1374 entity.associated_keys,
1375 entity.action_thresholds,
1376 )
1377 }
1378}
1379
1380impl AddressableEntity {
1381 #[allow(clippy::too_many_arguments)]
1383 pub fn new(
1384 package_hash: PackageHash,
1385 byte_code_hash: ByteCodeHash,
1386 protocol_version: ProtocolVersion,
1387 main_purse: URef,
1388 associated_keys: AssociatedKeys,
1389 action_thresholds: ActionThresholds,
1390 entity_kind: EntityKind,
1391 ) -> Self {
1392 AddressableEntity {
1393 package_hash,
1394 byte_code_hash,
1395 protocol_version,
1396 main_purse,
1397 action_thresholds,
1398 associated_keys,
1399 entity_kind,
1400 }
1401 }
1402
1403 pub fn entity_addr(&self, entity_hash: AddressableEntityHash) -> EntityAddr {
1405 let hash_addr = entity_hash.value();
1406 match self.entity_kind {
1407 EntityKind::System(_) => EntityAddr::new_system(hash_addr),
1408 EntityKind::Account(_) => EntityAddr::new_account(hash_addr),
1409 EntityKind::SmartContract(_) => EntityAddr::new_smart_contract(hash_addr),
1410 }
1411 }
1412
1413 pub fn entity_kind(&self) -> EntityKind {
1414 self.entity_kind
1415 }
1416
1417 pub fn package_hash(&self) -> PackageHash {
1419 self.package_hash
1420 }
1421
1422 pub fn byte_code_hash(&self) -> ByteCodeHash {
1424 self.byte_code_hash
1425 }
1426
1427 pub fn protocol_version(&self) -> ProtocolVersion {
1429 self.protocol_version
1430 }
1431
1432 pub fn main_purse(&self) -> URef {
1434 self.main_purse
1435 }
1436
1437 pub fn main_purse_add_only(&self) -> URef {
1439 URef::new(self.main_purse.addr(), AccessRights::ADD)
1440 }
1441
1442 pub fn associated_keys(&self) -> &AssociatedKeys {
1444 &self.associated_keys
1445 }
1446
1447 pub fn action_thresholds(&self) -> &ActionThresholds {
1449 &self.action_thresholds
1450 }
1451
1452 pub fn add_associated_key(
1454 &mut self,
1455 account_hash: AccountHash,
1456 weight: Weight,
1457 ) -> Result<(), AddKeyFailure> {
1458 self.associated_keys.add_key(account_hash, weight)
1459 }
1460
1461 fn can_remove_key(&self, account_hash: AccountHash) -> bool {
1463 let total_weight_without = self
1464 .associated_keys
1465 .total_keys_weight_excluding(account_hash);
1466
1467 total_weight_without >= *self.action_thresholds().deployment()
1470 && total_weight_without >= *self.action_thresholds().key_management()
1471 }
1472
1473 fn can_update_key(&self, account_hash: AccountHash, weight: Weight) -> bool {
1476 let total_weight = self
1478 .associated_keys
1479 .total_keys_weight_excluding(account_hash);
1480
1481 let new_weight = total_weight.value().saturating_add(weight.value());
1483
1484 new_weight >= self.action_thresholds().deployment().value()
1487 && new_weight >= self.action_thresholds().key_management().value()
1488 }
1489
1490 pub fn remove_associated_key(
1495 &mut self,
1496 account_hash: AccountHash,
1497 ) -> Result<(), RemoveKeyFailure> {
1498 if self.associated_keys.contains_key(&account_hash) {
1499 if !self.can_remove_key(account_hash) {
1501 return Err(RemoveKeyFailure::ThresholdViolation);
1502 }
1503 }
1504 self.associated_keys.remove_key(&account_hash)
1505 }
1506
1507 pub fn update_associated_key(
1511 &mut self,
1512 account_hash: AccountHash,
1513 weight: Weight,
1514 ) -> Result<(), UpdateKeyFailure> {
1515 if let Some(current_weight) = self.associated_keys.get(&account_hash) {
1516 if weight < *current_weight {
1517 if !self.can_update_key(account_hash, weight) {
1519 return Err(UpdateKeyFailure::ThresholdViolation);
1520 }
1521 }
1522 }
1523 self.associated_keys.update_key(account_hash, weight)
1524 }
1525
1526 pub fn set_action_threshold(
1531 &mut self,
1532 action_type: ActionType,
1533 weight: Weight,
1534 ) -> Result<(), SetThresholdFailure> {
1535 self.can_set_threshold(weight)?;
1538 self.action_thresholds.set_threshold(action_type, weight)
1540 }
1541
1542 pub fn set_action_threshold_unchecked(
1550 &mut self,
1551 action_type: ActionType,
1552 threshold: Weight,
1553 ) -> Result<(), SetThresholdFailure> {
1554 self.action_thresholds.set_threshold(action_type, threshold)
1555 }
1556
1557 pub fn can_set_threshold(&self, new_threshold: Weight) -> Result<(), SetThresholdFailure> {
1559 let total_weight = self.associated_keys.total_keys_weight();
1560 if new_threshold > total_weight {
1561 return Err(SetThresholdFailure::InsufficientTotalWeight);
1562 }
1563 Ok(())
1564 }
1565
1566 pub fn can_authorize(&self, authorization_keys: &BTreeSet<AccountHash>) -> bool {
1568 !authorization_keys.is_empty()
1569 && authorization_keys
1570 .iter()
1571 .any(|e| self.associated_keys.contains_key(e))
1572 }
1573
1574 pub fn can_deploy_with(&self, authorization_keys: &BTreeSet<AccountHash>) -> bool {
1577 let total_weight = self
1578 .associated_keys
1579 .calculate_keys_weight(authorization_keys);
1580
1581 total_weight >= *self.action_thresholds().deployment()
1582 }
1583
1584 pub fn can_manage_keys_with(&self, authorization_keys: &BTreeSet<AccountHash>) -> bool {
1587 let total_weight = self
1588 .associated_keys
1589 .calculate_keys_weight(authorization_keys);
1590
1591 total_weight >= *self.action_thresholds().key_management()
1592 }
1593
1594 pub fn can_upgrade_with(&self, authorization_keys: &BTreeSet<AccountHash>) -> bool {
1597 let total_weight = self
1598 .associated_keys
1599 .calculate_keys_weight(authorization_keys);
1600
1601 total_weight >= *self.action_thresholds().upgrade_management()
1602 }
1603
1604 pub fn byte_code_addr(&self) -> HashAddr {
1606 self.byte_code_hash.value()
1607 }
1608
1609 pub fn set_protocol_version(&mut self, protocol_version: ProtocolVersion) {
1611 self.protocol_version = protocol_version;
1612 }
1613
1614 pub fn is_compatible_protocol_version(&self, protocol_version: ProtocolVersion) -> bool {
1616 let entity_protocol_version = self.protocol_version.value();
1617 let context_protocol_version = protocol_version.value();
1618 if entity_protocol_version.major == context_protocol_version.major {
1619 return true;
1620 }
1621 if entity_protocol_version.major == 1 && context_protocol_version.major == 2 {
1622 return true;
1624 }
1625 false
1626 }
1627
1628 pub fn kind(&self) -> EntityKind {
1630 self.entity_kind
1631 }
1632
1633 pub fn is_account_kind(&self) -> bool {
1635 matches!(self.entity_kind, EntityKind::Account(_))
1636 }
1637
1638 pub fn entity_key(&self, entity_hash: AddressableEntityHash) -> Key {
1640 match self.entity_kind {
1641 EntityKind::System(_) => {
1642 Key::addressable_entity_key(EntityKindTag::System, entity_hash)
1643 }
1644 EntityKind::Account(_) => {
1645 Key::addressable_entity_key(EntityKindTag::Account, entity_hash)
1646 }
1647 EntityKind::SmartContract(_) => {
1648 Key::addressable_entity_key(EntityKindTag::SmartContract, entity_hash)
1649 }
1650 }
1651 }
1652
1653 pub fn extract_access_rights(
1655 &self,
1656 entity_hash: AddressableEntityHash,
1657 named_keys: &NamedKeys,
1658 ) -> ContextAccessRights {
1659 let urefs_iter = named_keys
1660 .keys()
1661 .filter_map(|key| key.as_uref().copied())
1662 .chain(iter::once(self.main_purse));
1663 ContextAccessRights::new(entity_hash.value(), urefs_iter)
1664 }
1665
1666 #[doc(hidden)]
1668 #[cfg(feature = "json-schema")]
1669 pub fn example() -> &'static Self {
1670 &ADDRESSABLE_ENTITY
1671 }
1672}
1673
1674impl ToBytes for AddressableEntity {
1675 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
1676 let mut result = bytesrepr::allocate_buffer(self)?;
1677 self.package_hash().write_bytes(&mut result)?;
1678 self.byte_code_hash().write_bytes(&mut result)?;
1679 self.protocol_version().write_bytes(&mut result)?;
1680 self.main_purse().write_bytes(&mut result)?;
1681 self.associated_keys().write_bytes(&mut result)?;
1682 self.action_thresholds().write_bytes(&mut result)?;
1683 self.kind().write_bytes(&mut result)?;
1684 Ok(result)
1685 }
1686
1687 fn serialized_length(&self) -> usize {
1688 ToBytes::serialized_length(&self.package_hash)
1689 + ToBytes::serialized_length(&self.byte_code_hash)
1690 + ToBytes::serialized_length(&self.protocol_version)
1691 + ToBytes::serialized_length(&self.main_purse)
1692 + ToBytes::serialized_length(&self.associated_keys)
1693 + ToBytes::serialized_length(&self.action_thresholds)
1694 + ToBytes::serialized_length(&self.entity_kind)
1695 }
1696
1697 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
1698 self.package_hash().write_bytes(writer)?;
1699 self.byte_code_hash().write_bytes(writer)?;
1700 self.protocol_version().write_bytes(writer)?;
1701 self.main_purse().write_bytes(writer)?;
1702 self.associated_keys().write_bytes(writer)?;
1703 self.action_thresholds().write_bytes(writer)?;
1704 self.kind().write_bytes(writer)?;
1705 Ok(())
1706 }
1707}
1708
1709impl FromBytes for AddressableEntity {
1710 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
1711 let (package_hash, bytes) = PackageHash::from_bytes(bytes)?;
1712 let (byte_code_hash, bytes) = ByteCodeHash::from_bytes(bytes)?;
1713 let (protocol_version, bytes) = ProtocolVersion::from_bytes(bytes)?;
1714 let (main_purse, bytes) = URef::from_bytes(bytes)?;
1715 let (associated_keys, bytes) = AssociatedKeys::from_bytes(bytes)?;
1716 let (action_thresholds, bytes) = ActionThresholds::from_bytes(bytes)?;
1717 let (entity_kind, bytes) = EntityKind::from_bytes(bytes)?;
1718 Ok((
1719 AddressableEntity {
1720 package_hash,
1721 byte_code_hash,
1722 protocol_version,
1723 main_purse,
1724 associated_keys,
1725 action_thresholds,
1726 entity_kind,
1727 },
1728 bytes,
1729 ))
1730 }
1731}
1732
1733impl Default for AddressableEntity {
1734 fn default() -> Self {
1735 AddressableEntity {
1736 byte_code_hash: [0; KEY_HASH_LENGTH].into(),
1737 package_hash: [0; KEY_HASH_LENGTH].into(),
1738 protocol_version: ProtocolVersion::V1_0_0,
1739 main_purse: URef::default(),
1740 action_thresholds: ActionThresholds::default(),
1741 associated_keys: AssociatedKeys::default(),
1742 entity_kind: EntityKind::SmartContract(ContractRuntimeTag::VmCasperV1),
1743 }
1744 }
1745}
1746
1747impl From<Contract> for AddressableEntity {
1748 fn from(value: Contract) -> Self {
1749 AddressableEntity::new(
1750 PackageHash::new(value.contract_package_hash().value()),
1751 ByteCodeHash::new(value.contract_wasm_hash().value()),
1752 value.protocol_version(),
1753 URef::default(),
1754 AssociatedKeys::default(),
1755 ActionThresholds::default(),
1756 EntityKind::SmartContract(ContractRuntimeTag::VmCasperV1),
1757 )
1758 }
1759}
1760
1761impl From<Account> for AddressableEntity {
1762 fn from(value: Account) -> Self {
1763 AddressableEntity::new(
1764 PackageHash::default(),
1765 ByteCodeHash::new([0u8; 32]),
1766 ProtocolVersion::default(),
1767 value.main_purse(),
1768 value.associated_keys().clone().into(),
1769 value.action_thresholds().clone().into(),
1770 EntityKind::Account(value.account_hash()),
1771 )
1772 }
1773}
1774
1775#[cfg(test)]
1776mod tests {
1777 use super::*;
1778 use crate::{AccessRights, URef, UREF_ADDR_LENGTH};
1779
1780 #[cfg(feature = "json-schema")]
1781 use schemars::{gen::SchemaGenerator, schema::InstanceType};
1782
1783 #[test]
1784 fn entity_hash_from_slice() {
1785 let bytes: Vec<u8> = (0..32).collect();
1786 let entity_hash = HashAddr::try_from(&bytes[..]).expect("should create contract hash");
1787 let entity_hash = AddressableEntityHash::new(entity_hash);
1788 assert_eq!(&bytes, &entity_hash.as_bytes());
1789 }
1790
1791 #[test]
1792 fn entity_hash_from_str() {
1793 let entity_hash = AddressableEntityHash([3; 32]);
1794 let encoded = entity_hash.to_formatted_string();
1795 let decoded = AddressableEntityHash::from_formatted_str(&encoded).unwrap();
1796 assert_eq!(entity_hash, decoded);
1797
1798 let invalid_prefix =
1799 "addressable-entity--0000000000000000000000000000000000000000000000000000000000000000";
1800 assert!(AddressableEntityHash::from_formatted_str(invalid_prefix).is_err());
1801
1802 let short_addr =
1803 "addressable-entity-00000000000000000000000000000000000000000000000000000000000000";
1804 assert!(AddressableEntityHash::from_formatted_str(short_addr).is_err());
1805
1806 let long_addr =
1807 "addressable-entity-000000000000000000000000000000000000000000000000000000000000000000";
1808 assert!(AddressableEntityHash::from_formatted_str(long_addr).is_err());
1809
1810 let invalid_hex =
1811 "addressable-entity-000000000000000000000000000000000000000000000000000000000000000g";
1812 assert!(AddressableEntityHash::from_formatted_str(invalid_hex).is_err());
1813 }
1814
1815 #[test]
1816 fn named_key_addr_from_str() {
1817 let named_key_addr =
1818 NamedKeyAddr::new_named_key_entry(EntityAddr::new_smart_contract([3; 32]), [4; 32]);
1819 let encoded = named_key_addr.to_formatted_string();
1820 let decoded = NamedKeyAddr::from_formatted_str(&encoded).unwrap();
1821 assert_eq!(named_key_addr, decoded);
1822 }
1823
1824 #[test]
1825 fn entity_hash_serde_roundtrip() {
1826 let entity_hash = AddressableEntityHash([255; 32]);
1827 let serialized = bincode::serialize(&entity_hash).unwrap();
1828 let deserialized = bincode::deserialize(&serialized).unwrap();
1829 assert_eq!(entity_hash, deserialized)
1830 }
1831
1832 #[test]
1833 fn entity_hash_json_roundtrip() {
1834 let entity_hash = AddressableEntityHash([255; 32]);
1835 let json_string = serde_json::to_string_pretty(&entity_hash).unwrap();
1836 let decoded = serde_json::from_str(&json_string).unwrap();
1837 assert_eq!(entity_hash, decoded)
1838 }
1839
1840 #[test]
1841 fn entity_addr_formatted_string_roundtrip() {
1842 let entity_addr = EntityAddr::Account([5; 32]);
1843 let encoded = entity_addr.to_formatted_string();
1844 let decoded = EntityAddr::from_formatted_str(&encoded).expect("must get entity addr");
1845 assert_eq!(decoded, entity_addr);
1846
1847 let entity_addr = EntityAddr::SmartContract([5; 32]);
1848 let encoded = entity_addr.to_formatted_string();
1849 let decoded = EntityAddr::from_formatted_str(&encoded).expect("must get entity addr");
1850 assert_eq!(decoded, entity_addr);
1851
1852 let entity_addr = EntityAddr::System([5; 32]);
1853 let encoded = entity_addr.to_formatted_string();
1854 let decoded = EntityAddr::from_formatted_str(&encoded).expect("must get entity addr");
1855 assert_eq!(decoded, entity_addr);
1856 }
1857
1858 #[test]
1859 fn entity_addr_serialization_roundtrip() {
1860 for addr in [
1861 EntityAddr::new_system([1; 32]),
1862 EntityAddr::new_account([1; 32]),
1863 EntityAddr::new_smart_contract([1; 32]),
1864 ] {
1865 bytesrepr::test_serialization_roundtrip(&addr);
1866 }
1867 }
1868
1869 #[test]
1870 fn entity_addr_serde_roundtrip() {
1871 for addr in [
1872 EntityAddr::new_system([1; 32]),
1873 EntityAddr::new_account([1; 32]),
1874 EntityAddr::new_smart_contract([1; 32]),
1875 ] {
1876 let serialized = bincode::serialize(&addr).unwrap();
1877 let deserialized = bincode::deserialize(&serialized).unwrap();
1878 assert_eq!(addr, deserialized)
1879 }
1880 }
1881
1882 #[test]
1883 fn entity_addr_json_roundtrip() {
1884 for addr in [
1885 EntityAddr::new_system([1; 32]),
1886 EntityAddr::new_account([1; 32]),
1887 EntityAddr::new_smart_contract([1; 32]),
1888 ] {
1889 let json_string = serde_json::to_string_pretty(&addr).unwrap();
1890 let decoded = serde_json::from_str(&json_string).unwrap();
1891 assert_eq!(addr, decoded)
1892 }
1893 }
1894
1895 #[cfg(feature = "json-schema")]
1896 #[test]
1897 fn entity_addr_schema() {
1898 let mut gen = SchemaGenerator::default();
1899 let any_of = EntityAddr::json_schema(&mut gen)
1900 .into_object()
1901 .subschemas
1902 .expect("should have subschemas")
1903 .any_of
1904 .expect("should have any_of");
1905 for elem in any_of {
1906 let schema = elem
1907 .into_object()
1908 .instance_type
1909 .expect("should have instance type");
1910 assert!(schema.contains(&InstanceType::String), "{:?}", schema);
1911 }
1912 }
1913
1914 #[test]
1915 fn should_extract_access_rights() {
1916 const MAIN_PURSE: URef = URef::new([2; 32], AccessRights::READ_ADD_WRITE);
1917
1918 let entity_hash = AddressableEntityHash([255; 32]);
1919 let uref = URef::new([84; UREF_ADDR_LENGTH], AccessRights::READ_ADD);
1920 let uref_r = URef::new([42; UREF_ADDR_LENGTH], AccessRights::READ);
1921 let uref_a = URef::new([42; UREF_ADDR_LENGTH], AccessRights::ADD);
1922 let uref_w = URef::new([42; UREF_ADDR_LENGTH], AccessRights::WRITE);
1923 let mut named_keys = NamedKeys::new();
1924 named_keys.insert("a".to_string(), Key::URef(uref_r));
1925 named_keys.insert("b".to_string(), Key::URef(uref_a));
1926 named_keys.insert("c".to_string(), Key::URef(uref_w));
1927 named_keys.insert("d".to_string(), Key::URef(uref));
1928 let associated_keys = AssociatedKeys::new(AccountHash::new([254; 32]), Weight::new(1));
1929 let contract = AddressableEntity::new(
1930 PackageHash::new([254; 32]),
1931 ByteCodeHash::new([253; 32]),
1932 ProtocolVersion::V1_0_0,
1933 MAIN_PURSE,
1934 associated_keys,
1935 ActionThresholds::new(Weight::new(1), Weight::new(1), Weight::new(1))
1936 .expect("should create thresholds"),
1937 EntityKind::SmartContract(ContractRuntimeTag::VmCasperV1),
1938 );
1939 let access_rights = contract.extract_access_rights(entity_hash, &named_keys);
1940 let expected_uref = URef::new([42; UREF_ADDR_LENGTH], AccessRights::READ_ADD_WRITE);
1941 assert!(
1942 access_rights.has_access_rights_to_uref(&uref),
1943 "urefs in named keys should be included in access rights"
1944 );
1945 assert!(
1946 access_rights.has_access_rights_to_uref(&expected_uref),
1947 "multiple access right bits to the same uref should coalesce"
1948 );
1949 }
1950}
1951
1952#[cfg(test)]
1953mod prop_tests {
1954 use proptest::prelude::*;
1955
1956 use crate::{bytesrepr, gens};
1957
1958 proptest! {
1959 #[test]
1960 fn test_value_contract(contract in gens::addressable_entity_arb()) {
1961 bytesrepr::test_serialization_roundtrip(&contract);
1962 }
1963 }
1964}