miden_objects/account/account_id/v0/
prefix.rs

1use alloc::string::{String, ToString};
2use core::fmt;
3use core::hash::Hash;
4
5use miden_core::Felt;
6use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
7use miden_processor::DeserializationError;
8
9use crate::account::account_id::v0::{self, validate_prefix};
10use crate::account::{AccountIdVersion, AccountStorageMode, AccountType};
11use crate::errors::AccountIdError;
12
13// ACCOUNT ID PREFIX VERSION 0
14// ================================================================================================
15
16/// The prefix of an [`AccountIdV0`](crate::account::AccountIdV0), i.e. its first field element.
17///
18/// See the [`AccountId`](crate::account::AccountId)'s documentation for details.
19#[derive(Debug, Copy, Clone, Eq, PartialEq)]
20pub struct AccountIdPrefixV0 {
21    prefix: Felt,
22}
23
24impl Hash for AccountIdPrefixV0 {
25    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
26        self.prefix.inner().hash(state);
27    }
28}
29
30impl AccountIdPrefixV0 {
31    // CONSTANTS
32    // --------------------------------------------------------------------------------------------
33
34    /// The serialized size of an [`AccountIdPrefixV0`] in bytes.
35    const SERIALIZED_SIZE: usize = 8;
36
37    // CONSTRUCTORS
38    // --------------------------------------------------------------------------------------------
39
40    /// See [`AccountIdPrefix::new_unchecked`](crate::account::AccountIdPrefix::new_unchecked) for
41    /// details.
42    pub fn new_unchecked(prefix: Felt) -> Self {
43        // Panic on invalid felts in debug mode.
44        if cfg!(debug_assertions) {
45            validate_prefix(prefix)
46                .expect("AccountIdPrefix::new_unchecked called with invalid prefix");
47        }
48
49        AccountIdPrefixV0 { prefix }
50    }
51
52    /// See [`AccountIdPrefix::new`](crate::account::AccountIdPrefix::new) for details.
53    pub fn new(prefix: Felt) -> Result<Self, AccountIdError> {
54        validate_prefix(prefix)?;
55
56        Ok(AccountIdPrefixV0 { prefix })
57    }
58
59    // PUBLIC ACCESSORS
60    // --------------------------------------------------------------------------------------------
61
62    /// See [`AccountIdPrefix::as_felt`](crate::account::AccountIdPrefix::as_felt) for details.
63    pub const fn as_felt(&self) -> Felt {
64        self.prefix
65    }
66
67    /// See [`AccountIdPrefix::as_u64`](crate::account::AccountIdPrefix::as_u64) for details.
68    pub const fn as_u64(&self) -> u64 {
69        self.prefix.as_int()
70    }
71
72    /// See [`AccountIdPrefix::account_type`](crate::account::AccountIdPrefix::account_type) for
73    /// details.
74    pub const fn account_type(&self) -> AccountType {
75        v0::extract_type(self.prefix.as_int())
76    }
77
78    /// See [`AccountIdPrefix::is_faucet`](crate::account::AccountIdPrefix::is_faucet) for details.
79    pub fn is_faucet(&self) -> bool {
80        self.account_type().is_faucet()
81    }
82
83    /// See [`AccountIdPrefix::is_regular_account`](crate::account::AccountIdPrefix::is_regular_account) for
84    /// details.
85    pub fn is_regular_account(&self) -> bool {
86        self.account_type().is_regular_account()
87    }
88
89    /// See [`AccountIdPrefix::storage_mode`](crate::account::AccountIdPrefix::storage_mode) for
90    /// details.
91    pub fn storage_mode(&self) -> AccountStorageMode {
92        v0::extract_storage_mode(self.prefix.as_int())
93            .expect("account ID prefix should have been constructed with a valid storage mode")
94    }
95
96    /// See [`AccountIdPrefix::is_public`](crate::account::AccountIdPrefix::is_public) for details.
97    pub fn is_public(&self) -> bool {
98        self.storage_mode().is_public()
99    }
100
101    /// See [`AccountIdPrefix::version`](crate::account::AccountIdPrefix::version) for details.
102    pub fn version(&self) -> AccountIdVersion {
103        v0::extract_version(self.prefix.as_int())
104            .expect("account ID prefix should have been constructed with a valid version")
105    }
106
107    /// See [`AccountIdPrefix::to_hex`](crate::account::AccountIdPrefix::to_hex) for details.
108    pub fn to_hex(self) -> String {
109        format!("0x{:016x}", self.prefix.as_int())
110    }
111}
112
113// CONVERSIONS FROM ACCOUNT ID PREFIX
114// ================================================================================================
115
116impl From<AccountIdPrefixV0> for Felt {
117    fn from(id: AccountIdPrefixV0) -> Self {
118        id.prefix
119    }
120}
121
122impl From<AccountIdPrefixV0> for [u8; 8] {
123    fn from(id: AccountIdPrefixV0) -> Self {
124        let mut result = [0_u8; 8];
125        result[..8].copy_from_slice(&id.prefix.as_int().to_be_bytes());
126        result
127    }
128}
129
130impl From<AccountIdPrefixV0> for u64 {
131    fn from(id: AccountIdPrefixV0) -> Self {
132        id.prefix.as_int()
133    }
134}
135
136// CONVERSIONS TO ACCOUNT ID PREFIX
137// ================================================================================================
138
139impl TryFrom<[u8; 8]> for AccountIdPrefixV0 {
140    type Error = AccountIdError;
141
142    /// See [`TryFrom<[u8; 8]> for
143    /// AccountIdPrefix`](crate::account::AccountIdPrefix#impl-TryFrom<%5Bu8;+8%
144    /// 5D>-for-AccountIdPrefix) for details.
145    fn try_from(mut value: [u8; 8]) -> Result<Self, Self::Error> {
146        // Felt::try_from expects little-endian order.
147        value.reverse();
148
149        Felt::try_from(value.as_slice())
150            .map_err(AccountIdError::AccountIdInvalidPrefixFieldElement)
151            .and_then(Self::new)
152    }
153}
154
155impl TryFrom<u64> for AccountIdPrefixV0 {
156    type Error = AccountIdError;
157
158    /// See [`TryFrom<u64> for
159    /// AccountIdPrefix`](crate::account::AccountIdPrefix#impl-TryFrom<u64>-for-AccountIdPrefix)
160    /// for details.
161    fn try_from(value: u64) -> Result<Self, Self::Error> {
162        let element = Felt::try_from(value.to_le_bytes().as_slice())
163            .map_err(AccountIdError::AccountIdInvalidPrefixFieldElement)?;
164        Self::new(element)
165    }
166}
167
168impl TryFrom<Felt> for AccountIdPrefixV0 {
169    type Error = AccountIdError;
170
171    /// See [`TryFrom<Felt> for
172    /// AccountIdPrefix`](crate::account::AccountIdPrefix#impl-TryFrom<Felt>-for-AccountIdPrefix)
173    /// for details.
174    fn try_from(element: Felt) -> Result<Self, Self::Error> {
175        Self::new(element)
176    }
177}
178
179// COMMON TRAIT IMPLS
180// ================================================================================================
181
182impl PartialOrd for AccountIdPrefixV0 {
183    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
184        Some(self.cmp(other))
185    }
186}
187
188impl Ord for AccountIdPrefixV0 {
189    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
190        self.prefix.as_int().cmp(&other.prefix.as_int())
191    }
192}
193
194impl fmt::Display for AccountIdPrefixV0 {
195    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196        write!(f, "{}", self.to_hex())
197    }
198}
199
200// SERIALIZATION
201// ================================================================================================
202
203impl Serializable for AccountIdPrefixV0 {
204    fn write_into<W: ByteWriter>(&self, target: &mut W) {
205        let bytes: [u8; 8] = (*self).into();
206        bytes.write_into(target);
207    }
208
209    fn get_size_hint(&self) -> usize {
210        Self::SERIALIZED_SIZE
211    }
212}
213
214impl Deserializable for AccountIdPrefixV0 {
215    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
216        <[u8; 8]>::read_from(source)?
217            .try_into()
218            .map_err(|err: AccountIdError| DeserializationError::InvalidValue(err.to_string()))
219    }
220}
221
222// TESTS
223// ================================================================================================
224
225#[cfg(test)]
226mod tests {
227    use super::*;
228    use crate::account::{AccountId, AccountIdPrefix};
229    use crate::testing::account_id::{
230        ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET,
231        ACCOUNT_ID_PRIVATE_SENDER,
232        ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
233        ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE,
234        ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
235    };
236
237    #[test]
238    fn test_account_id_prefix_conversion_roundtrip() {
239        for (idx, account_id) in [
240            ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
241            ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE,
242            ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
243            ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET,
244            ACCOUNT_ID_PRIVATE_SENDER,
245        ]
246        .into_iter()
247        .enumerate()
248        {
249            let full_id = AccountId::try_from(account_id).unwrap();
250            let prefix = full_id.prefix();
251            assert_eq!(
252                prefix,
253                AccountIdPrefix::read_from_bytes(&prefix.to_bytes()).unwrap(),
254                "failed in {idx}"
255            );
256        }
257    }
258}