miden_protocol/account/account_id/v1/
prefix.rs1use alloc::string::{String, ToString};
2use core::fmt;
3use core::hash::Hash;
4
5use miden_core::Felt;
6
7use crate::account::account_id::v1::{self, validate_prefix};
8use crate::account::{AccountIdVersion, 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 AccountIdPrefixV1 {
26 prefix: Felt,
27}
28
29impl Hash for AccountIdPrefixV1 {
30 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
31 self.prefix.as_canonical_u64().hash(state);
32 }
33}
34
35impl AccountIdPrefixV1 {
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 AccountIdPrefixV1 { prefix }
55 }
56
57 pub fn new(prefix: Felt) -> Result<Self, AccountIdError> {
59 validate_prefix(prefix)?;
60
61 Ok(AccountIdPrefixV1 { 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 v1::extract_account_type(self.prefix.as_canonical_u64())
81 }
82
83 pub fn is_public(&self) -> bool {
85 self.account_type().is_public()
86 }
87
88 pub fn version(&self) -> AccountIdVersion {
90 v1::extract_version(self.prefix.as_canonical_u64())
91 .expect("account ID prefix should have been constructed with a valid version")
92 }
93
94 pub fn to_hex(self) -> String {
96 format!("0x{:016x}", self.prefix.as_canonical_u64())
97 }
98}
99
100impl From<AccountIdPrefixV1> for Felt {
104 fn from(id: AccountIdPrefixV1) -> Self {
105 id.prefix
106 }
107}
108
109impl From<AccountIdPrefixV1> for [u8; 8] {
110 fn from(id: AccountIdPrefixV1) -> Self {
111 let mut result = [0_u8; 8];
112 result[..8].copy_from_slice(&id.prefix.as_canonical_u64().to_be_bytes());
113 result
114 }
115}
116
117impl From<AccountIdPrefixV1> for u64 {
118 fn from(id: AccountIdPrefixV1) -> Self {
119 id.prefix.as_canonical_u64()
120 }
121}
122
123impl TryFrom<[u8; 8]> for AccountIdPrefixV1 {
127 type Error = AccountIdError;
128
129 fn try_from(mut value: [u8; 8]) -> Result<Self, Self::Error> {
133 value.reverse();
135
136 let num = u64::from_le_bytes(value);
137 Felt::try_from(num)
138 .map_err(|err| {
139 AccountIdError::AccountIdInvalidPrefixFieldElement(
140 DeserializationError::InvalidValue(err.to_string()),
141 )
142 })
143 .and_then(Self::new)
144 }
145}
146
147impl TryFrom<u64> for AccountIdPrefixV1 {
148 type Error = AccountIdError;
149
150 fn try_from(value: u64) -> Result<Self, Self::Error> {
154 let element = Felt::try_from(value).map_err(|err| {
155 AccountIdError::AccountIdInvalidPrefixFieldElement(DeserializationError::InvalidValue(
156 err.to_string(),
157 ))
158 })?;
159 Self::new(element)
160 }
161}
162
163impl TryFrom<Felt> for AccountIdPrefixV1 {
164 type Error = AccountIdError;
165
166 fn try_from(element: Felt) -> Result<Self, Self::Error> {
170 Self::new(element)
171 }
172}
173
174impl PartialOrd for AccountIdPrefixV1 {
178 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
179 Some(self.cmp(other))
180 }
181}
182
183impl Ord for AccountIdPrefixV1 {
184 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
185 self.prefix.as_canonical_u64().cmp(&other.prefix.as_canonical_u64())
186 }
187}
188
189impl fmt::Display for AccountIdPrefixV1 {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 write!(f, "{}", self.to_hex())
192 }
193}
194
195impl Serializable for AccountIdPrefixV1 {
199 fn write_into<W: ByteWriter>(&self, target: &mut W) {
200 let bytes: [u8; 8] = (*self).into();
201 bytes.write_into(target);
202 }
203
204 fn get_size_hint(&self) -> usize {
205 Self::SERIALIZED_SIZE
206 }
207}
208
209impl Deserializable for AccountIdPrefixV1 {
210 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
211 <[u8; 8]>::read_from(source)?
212 .try_into()
213 .map_err(|err: AccountIdError| DeserializationError::InvalidValue(err.to_string()))
214 }
215}
216
217#[cfg(test)]
221mod tests {
222 use super::*;
223 use crate::account::{AccountId, AccountIdPrefix};
224 use crate::testing::account_id::{
225 ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET,
226 ACCOUNT_ID_PRIVATE_SENDER,
227 ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
228 ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE,
229 ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
230 };
231
232 #[test]
233 fn test_account_id_prefix_conversion_roundtrip() {
234 for (idx, account_id) in [
235 ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
236 ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE,
237 ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
238 ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET,
239 ACCOUNT_ID_PRIVATE_SENDER,
240 ]
241 .into_iter()
242 .enumerate()
243 {
244 let full_id = AccountId::try_from(account_id).unwrap();
245 let prefix = full_id.prefix();
246 assert_eq!(
247 prefix,
248 AccountIdPrefix::read_from_bytes(&prefix.to_bytes()).unwrap(),
249 "failed in {idx}"
250 );
251 }
252 }
253}