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