casper_types/account/
account_hash.rs1use alloc::{string::String, vec::Vec};
2use core::{
3 convert::{From, TryFrom},
4 fmt::{Debug, Display, Formatter},
5};
6#[cfg(feature = "datasize")]
7use datasize::DataSize;
8use rand::{
9 distributions::{Distribution, Standard},
10 Rng,
11};
12#[cfg(feature = "json-schema")]
13use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
14use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
15
16use super::FromStrError;
17use crate::{
18 bytesrepr::{Error, FromBytes, ToBytes},
19 checksummed_hex, crypto, CLType, CLTyped, PublicKey, BLAKE2B_DIGEST_LENGTH,
20};
21
22pub const ACCOUNT_HASH_LENGTH: usize = 32;
24pub const ACCOUNT_HASH_FORMATTED_STRING_PREFIX: &str = "account-hash-";
27
28#[derive(Default, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
31#[cfg_attr(feature = "datasize", derive(DataSize))]
32pub struct AccountHash(pub [u8; ACCOUNT_HASH_LENGTH]);
33
34impl AccountHash {
35 pub const fn new(value: [u8; ACCOUNT_HASH_LENGTH]) -> AccountHash {
37 AccountHash(value)
38 }
39
40 pub fn value(&self) -> [u8; ACCOUNT_HASH_LENGTH] {
42 self.0
43 }
44
45 pub fn as_bytes(&self) -> &[u8] {
47 &self.0
48 }
49
50 pub fn to_formatted_string(self) -> String {
52 format!(
53 "{}{}",
54 ACCOUNT_HASH_FORMATTED_STRING_PREFIX,
55 base16::encode_lower(&self.0),
56 )
57 }
58
59 pub fn from_formatted_str(input: &str) -> Result<Self, FromStrError> {
61 let remainder = input
62 .strip_prefix(ACCOUNT_HASH_FORMATTED_STRING_PREFIX)
63 .ok_or(FromStrError::InvalidPrefix)?;
64 let bytes =
65 <[u8; ACCOUNT_HASH_LENGTH]>::try_from(checksummed_hex::decode(remainder)?.as_ref())?;
66 Ok(AccountHash(bytes))
67 }
68
69 pub fn from_public_key(
71 public_key: &PublicKey,
72 blake2b_hash_fn: impl Fn(Vec<u8>) -> [u8; BLAKE2B_DIGEST_LENGTH],
73 ) -> Self {
74 const SYSTEM_LOWERCASE: &str = "system";
75 const ED25519_LOWERCASE: &str = "ed25519";
76 const SECP256K1_LOWERCASE: &str = "secp256k1";
77
78 let algorithm_name = match public_key {
79 PublicKey::System => SYSTEM_LOWERCASE,
80 PublicKey::Ed25519(_) => ED25519_LOWERCASE,
81 PublicKey::Secp256k1(_) => SECP256K1_LOWERCASE,
82 };
83 let public_key_bytes: Vec<u8> = public_key.into();
84
85 let preimage = {
87 let mut data = Vec::with_capacity(algorithm_name.len() + public_key_bytes.len() + 1);
88 data.extend(algorithm_name.as_bytes());
89 data.push(0);
90 data.extend(public_key_bytes);
91 data
92 };
93 let digest = blake2b_hash_fn(preimage);
95 Self::new(digest)
96 }
97}
98
99#[cfg(feature = "json-schema")]
100impl JsonSchema for AccountHash {
101 fn schema_name() -> String {
102 String::from("AccountHash")
103 }
104
105 fn json_schema(gen: &mut SchemaGenerator) -> Schema {
106 let schema = gen.subschema_for::<String>();
107 let mut schema_object = schema.into_object();
108 schema_object.metadata().description = Some("Hex-encoded account hash.".to_string());
109 schema_object.into()
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#[derive(Debug)]
212pub struct TryFromSliceForAccountHashError(());
213
214impl Distribution<AccountHash> for Standard {
215 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> AccountHash {
216 AccountHash::new(rng.gen())
217 }
218}