casper_types/account/
account_hash.rs

1use alloc::{string::String, vec::Vec};
2use core::{
3    convert::{From, TryFrom},
4    fmt::{Debug, Display, Formatter},
5};
6#[cfg(feature = "datasize")]
7use datasize::DataSize;
8#[cfg(any(feature = "testing", test))]
9use rand::{
10    distributions::{Distribution, Standard},
11    Rng,
12};
13#[cfg(feature = "json-schema")]
14use schemars::JsonSchema;
15use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
16
17use crate::{
18    addressable_entity::FromStrError,
19    bytesrepr::{Error, FromBytes, ToBytes},
20    checksummed_hex, crypto, CLType, CLTyped, PublicKey, BLAKE2B_DIGEST_LENGTH,
21};
22
23/// The length in bytes of a [`AccountHash`].
24pub const ACCOUNT_HASH_LENGTH: usize = 32;
25/// The prefix applied to the hex-encoded `AccountHash` to produce a formatted string
26/// representation.
27pub const ACCOUNT_HASH_FORMATTED_STRING_PREFIX: &str = "account-hash-";
28
29/// A newtype wrapping an array which contains the raw bytes of
30/// the AccountHash, a hash of Public Key and Algorithm
31#[derive(Default, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
32#[cfg_attr(feature = "datasize", derive(DataSize))]
33#[cfg_attr(
34    feature = "json-schema",
35    derive(JsonSchema),
36    schemars(description = "Account hash as a formatted string.")
37)]
38pub struct AccountHash(
39    #[cfg_attr(feature = "json-schema", schemars(skip, with = "String"))]
40    pub  [u8; ACCOUNT_HASH_LENGTH],
41);
42
43impl AccountHash {
44    /// Constructs a new `AccountHash` instance from the raw bytes of an Public Key Account Hash.
45    pub const fn new(value: [u8; ACCOUNT_HASH_LENGTH]) -> AccountHash {
46        AccountHash(value)
47    }
48
49    /// Returns the raw bytes of the account hash as an array.
50    pub fn value(&self) -> [u8; ACCOUNT_HASH_LENGTH] {
51        self.0
52    }
53
54    /// Returns the raw bytes of the account hash as a `slice`.
55    pub fn as_bytes(&self) -> &[u8] {
56        &self.0
57    }
58
59    /// Formats the `AccountHash` for users getting and putting.
60    pub fn to_formatted_string(self) -> String {
61        format!(
62            "{}{}",
63            ACCOUNT_HASH_FORMATTED_STRING_PREFIX,
64            base16::encode_lower(&self.0),
65        )
66    }
67
68    /// Hexadecimal representation of the hash.
69    pub fn to_hex_string(&self) -> String {
70        base16::encode_lower(&self.0)
71    }
72
73    /// Parses a string formatted as per `Self::to_formatted_string()` into an `AccountHash`.
74    pub fn from_formatted_str(input: &str) -> Result<Self, FromStrError> {
75        let remainder = input
76            .strip_prefix(ACCOUNT_HASH_FORMATTED_STRING_PREFIX)
77            .ok_or(FromStrError::InvalidPrefix)?;
78        let bytes =
79            <[u8; ACCOUNT_HASH_LENGTH]>::try_from(checksummed_hex::decode(remainder)?.as_ref())?;
80        Ok(AccountHash(bytes))
81    }
82
83    /// Parses a `PublicKey` and outputs the corresponding account hash.
84    pub fn from_public_key(
85        public_key: &PublicKey,
86        blake2b_hash_fn: impl Fn(Vec<u8>) -> [u8; BLAKE2B_DIGEST_LENGTH],
87    ) -> Self {
88        const SYSTEM_LOWERCASE: &str = "system";
89        const ED25519_LOWERCASE: &str = "ed25519";
90        const SECP256K1_LOWERCASE: &str = "secp256k1";
91
92        let algorithm_name = match public_key {
93            PublicKey::System => SYSTEM_LOWERCASE,
94            PublicKey::Ed25519(_) => ED25519_LOWERCASE,
95            PublicKey::Secp256k1(_) => SECP256K1_LOWERCASE,
96        };
97        let public_key_bytes: Vec<u8> = public_key.into();
98
99        // Prepare preimage based on the public key parameters.
100        let preimage = {
101            let mut data = Vec::with_capacity(algorithm_name.len() + public_key_bytes.len() + 1);
102            data.extend(algorithm_name.as_bytes());
103            data.push(0);
104            data.extend(public_key_bytes);
105            data
106        };
107        // Hash the preimage data using blake2b256 and return it.
108        let digest = blake2b_hash_fn(preimage);
109        Self::new(digest)
110    }
111}
112
113impl Serialize for AccountHash {
114    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
115        if serializer.is_human_readable() {
116            self.to_formatted_string().serialize(serializer)
117        } else {
118            self.0.serialize(serializer)
119        }
120    }
121}
122
123impl<'de> Deserialize<'de> for AccountHash {
124    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
125        if deserializer.is_human_readable() {
126            let formatted_string = String::deserialize(deserializer)?;
127            AccountHash::from_formatted_str(&formatted_string).map_err(SerdeError::custom)
128        } else {
129            let bytes = <[u8; ACCOUNT_HASH_LENGTH]>::deserialize(deserializer)?;
130            Ok(AccountHash(bytes))
131        }
132    }
133}
134
135impl TryFrom<&[u8]> for AccountHash {
136    type Error = TryFromSliceForAccountHashError;
137
138    fn try_from(bytes: &[u8]) -> Result<Self, TryFromSliceForAccountHashError> {
139        <[u8; ACCOUNT_HASH_LENGTH]>::try_from(bytes)
140            .map(AccountHash::new)
141            .map_err(|_| TryFromSliceForAccountHashError(()))
142    }
143}
144
145impl TryFrom<&alloc::vec::Vec<u8>> for AccountHash {
146    type Error = TryFromSliceForAccountHashError;
147
148    fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
149        <[u8; ACCOUNT_HASH_LENGTH]>::try_from(bytes as &[u8])
150            .map(AccountHash::new)
151            .map_err(|_| TryFromSliceForAccountHashError(()))
152    }
153}
154
155impl From<&PublicKey> for AccountHash {
156    fn from(public_key: &PublicKey) -> Self {
157        AccountHash::from_public_key(public_key, crypto::blake2b)
158    }
159}
160
161impl Display for AccountHash {
162    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
163        write!(f, "{}", base16::encode_lower(&self.0))
164    }
165}
166
167impl Debug for AccountHash {
168    fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
169        write!(f, "AccountHash({})", base16::encode_lower(&self.0))
170    }
171}
172
173impl CLTyped for AccountHash {
174    fn cl_type() -> CLType {
175        CLType::ByteArray(ACCOUNT_HASH_LENGTH as u32)
176    }
177}
178
179impl ToBytes for AccountHash {
180    #[inline(always)]
181    fn to_bytes(&self) -> Result<Vec<u8>, Error> {
182        self.0.to_bytes()
183    }
184
185    #[inline(always)]
186    fn serialized_length(&self) -> usize {
187        self.0.serialized_length()
188    }
189
190    #[inline(always)]
191    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), Error> {
192        writer.extend_from_slice(&self.0);
193        Ok(())
194    }
195}
196
197impl FromBytes for AccountHash {
198    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
199        let (bytes, rem) = FromBytes::from_bytes(bytes)?;
200        Ok((AccountHash::new(bytes), rem))
201    }
202}
203
204impl AsRef<[u8]> for AccountHash {
205    fn as_ref(&self) -> &[u8] {
206        self.0.as_ref()
207    }
208}
209
210/// Associated error type of `TryFrom<&[u8]>` for [`AccountHash`].
211#[derive(Debug)]
212pub struct TryFromSliceForAccountHashError(());
213
214#[cfg(any(feature = "testing", test))]
215impl Distribution<AccountHash> for Standard {
216    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> AccountHash {
217        AccountHash::new(rng.gen())
218    }
219}