miden_protocol/account/account_id/v0/
prefix.rs1use alloc::string::{String, ToString};
2use core::fmt;
3use core::hash::Hash;
4
5use miden_core::Felt;
6
7use crate::account::account_id::v0::{self, validate_prefix};
8use crate::account::{AccountIdVersion, AccountStorageMode, AccountType};
9use crate::errors::AccountIdError;
10use crate::utils::serde::{
11 ByteReader,
12 ByteWriter,
13 Deserializable,
14 DeserializationError,
15 Serializable,
16};
17
18#[derive(Debug, Copy, Clone, Eq, PartialEq)]
25pub struct AccountIdPrefixV0 {
26 prefix: Felt,
27}
28
29impl Hash for AccountIdPrefixV0 {
30 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
31 self.prefix.as_canonical_u64().hash(state);
32 }
33}
34
35impl AccountIdPrefixV0 {
36 const SERIALIZED_SIZE: usize = 8;
41
42 pub fn new_unchecked(prefix: Felt) -> Self {
48 if cfg!(debug_assertions) {
50 validate_prefix(prefix)
51 .expect("AccountIdPrefix::new_unchecked called with invalid prefix");
52 }
53
54 AccountIdPrefixV0 { prefix }
55 }
56
57 pub fn new(prefix: Felt) -> Result<Self, AccountIdError> {
59 validate_prefix(prefix)?;
60
61 Ok(AccountIdPrefixV0 { prefix })
62 }
63
64 pub const fn as_felt(&self) -> Felt {
69 self.prefix
70 }
71
72 pub fn as_u64(&self) -> u64 {
74 self.prefix.as_canonical_u64()
75 }
76
77 pub fn account_type(&self) -> AccountType {
80 v0::extract_type(self.prefix.as_canonical_u64())
81 }
82
83 pub fn is_faucet(&self) -> bool {
85 self.account_type().is_faucet()
86 }
87
88 pub fn is_regular_account(&self) -> bool {
91 self.account_type().is_regular_account()
92 }
93
94 pub fn storage_mode(&self) -> AccountStorageMode {
97 v0::extract_storage_mode(self.prefix.as_canonical_u64())
98 .expect("account ID prefix should have been constructed with a valid storage mode")
99 }
100
101 pub fn is_public(&self) -> bool {
103 self.storage_mode().is_public()
104 }
105
106 pub fn version(&self) -> AccountIdVersion {
108 v0::extract_version(self.prefix.as_canonical_u64())
109 .expect("account ID prefix should have been constructed with a valid version")
110 }
111
112 pub fn to_hex(self) -> String {
114 format!("0x{:016x}", self.prefix.as_canonical_u64())
115 }
116}
117
118impl From<AccountIdPrefixV0> for Felt {
122 fn from(id: AccountIdPrefixV0) -> Self {
123 id.prefix
124 }
125}
126
127impl From<AccountIdPrefixV0> for [u8; 8] {
128 fn from(id: AccountIdPrefixV0) -> Self {
129 let mut result = [0_u8; 8];
130 result[..8].copy_from_slice(&id.prefix.as_canonical_u64().to_be_bytes());
131 result
132 }
133}
134
135impl From<AccountIdPrefixV0> for u64 {
136 fn from(id: AccountIdPrefixV0) -> Self {
137 id.prefix.as_canonical_u64()
138 }
139}
140
141impl TryFrom<[u8; 8]> for AccountIdPrefixV0 {
145 type Error = AccountIdError;
146
147 fn try_from(mut value: [u8; 8]) -> Result<Self, Self::Error> {
151 value.reverse();
153
154 let num = u64::from_le_bytes(value);
155 Felt::try_from(num)
156 .map_err(|err| {
157 AccountIdError::AccountIdInvalidPrefixFieldElement(
158 DeserializationError::InvalidValue(err.to_string()),
159 )
160 })
161 .and_then(Self::new)
162 }
163}
164
165impl TryFrom<u64> for AccountIdPrefixV0 {
166 type Error = AccountIdError;
167
168 fn try_from(value: u64) -> Result<Self, Self::Error> {
172 let element = Felt::try_from(value).map_err(|err| {
173 AccountIdError::AccountIdInvalidPrefixFieldElement(DeserializationError::InvalidValue(
174 err.to_string(),
175 ))
176 })?;
177 Self::new(element)
178 }
179}
180
181impl TryFrom<Felt> for AccountIdPrefixV0 {
182 type Error = AccountIdError;
183
184 fn try_from(element: Felt) -> Result<Self, Self::Error> {
188 Self::new(element)
189 }
190}
191
192impl PartialOrd for AccountIdPrefixV0 {
196 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
197 Some(self.cmp(other))
198 }
199}
200
201impl Ord for AccountIdPrefixV0 {
202 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
203 self.prefix.as_canonical_u64().cmp(&other.prefix.as_canonical_u64())
204 }
205}
206
207impl fmt::Display for AccountIdPrefixV0 {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209 write!(f, "{}", self.to_hex())
210 }
211}
212
213impl Serializable for AccountIdPrefixV0 {
217 fn write_into<W: ByteWriter>(&self, target: &mut W) {
218 let bytes: [u8; 8] = (*self).into();
219 bytes.write_into(target);
220 }
221
222 fn get_size_hint(&self) -> usize {
223 Self::SERIALIZED_SIZE
224 }
225}
226
227impl Deserializable for AccountIdPrefixV0 {
228 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
229 <[u8; 8]>::read_from(source)?
230 .try_into()
231 .map_err(|err: AccountIdError| DeserializationError::InvalidValue(err.to_string()))
232 }
233}
234
235#[cfg(test)]
239mod tests {
240 use super::*;
241 use crate::account::{AccountId, AccountIdPrefix};
242 use crate::testing::account_id::{
243 ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET,
244 ACCOUNT_ID_PRIVATE_SENDER,
245 ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
246 ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE,
247 ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
248 };
249
250 #[test]
251 fn test_account_id_prefix_conversion_roundtrip() {
252 for (idx, account_id) in [
253 ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
254 ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE,
255 ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
256 ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET,
257 ACCOUNT_ID_PRIVATE_SENDER,
258 ]
259 .into_iter()
260 .enumerate()
261 {
262 let full_id = AccountId::try_from(account_id).unwrap();
263 let prefix = full_id.prefix();
264 assert_eq!(
265 prefix,
266 AccountIdPrefix::read_from_bytes(&prefix.to_bytes()).unwrap(),
267 "failed in {idx}"
268 );
269 }
270 }
271}