Skip to main content

miden_protocol/account/account_id/v0/
prefix.rs

1use 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// ACCOUNT ID PREFIX VERSION 0
19// ================================================================================================
20
21/// The prefix of an [`AccountIdV0`](crate::account::AccountIdV0), i.e. its first field element.
22///
23/// See the [`AccountId`](crate::account::AccountId)'s documentation for details.
24#[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    // CONSTANTS
37    // --------------------------------------------------------------------------------------------
38
39    /// The serialized size of an [`AccountIdPrefixV0`] in bytes.
40    const SERIALIZED_SIZE: usize = 8;
41
42    // CONSTRUCTORS
43    // --------------------------------------------------------------------------------------------
44
45    /// See [`AccountIdPrefix::new_unchecked`](crate::account::AccountIdPrefix::new_unchecked) for
46    /// details.
47    pub fn new_unchecked(prefix: Felt) -> Self {
48        // Panic on invalid felts in debug mode.
49        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    /// See [`AccountIdPrefix::new`](crate::account::AccountIdPrefix::new) for details.
58    pub fn new(prefix: Felt) -> Result<Self, AccountIdError> {
59        validate_prefix(prefix)?;
60
61        Ok(AccountIdPrefixV0 { prefix })
62    }
63
64    // PUBLIC ACCESSORS
65    // --------------------------------------------------------------------------------------------
66
67    /// See [`AccountIdPrefix::as_felt`](crate::account::AccountIdPrefix::as_felt) for details.
68    pub const fn as_felt(&self) -> Felt {
69        self.prefix
70    }
71
72    /// See [`AccountIdPrefix::as_u64`](crate::account::AccountIdPrefix::as_u64) for details.
73    pub fn as_u64(&self) -> u64 {
74        self.prefix.as_canonical_u64()
75    }
76
77    /// See [`AccountIdPrefix::account_type`](crate::account::AccountIdPrefix::account_type) for
78    /// details.
79    pub fn account_type(&self) -> AccountType {
80        v0::extract_type(self.prefix.as_canonical_u64())
81    }
82
83    /// See [`AccountIdPrefix::is_faucet`](crate::account::AccountIdPrefix::is_faucet) for details.
84    pub fn is_faucet(&self) -> bool {
85        self.account_type().is_faucet()
86    }
87
88    /// See [`AccountIdPrefix::is_regular_account`](crate::account::AccountIdPrefix::is_regular_account) for
89    /// details.
90    pub fn is_regular_account(&self) -> bool {
91        self.account_type().is_regular_account()
92    }
93
94    /// See [`AccountIdPrefix::storage_mode`](crate::account::AccountIdPrefix::storage_mode) for
95    /// details.
96    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    /// See [`AccountIdPrefix::is_public`](crate::account::AccountIdPrefix::is_public) for details.
102    pub fn is_public(&self) -> bool {
103        self.storage_mode().is_public()
104    }
105
106    /// See [`AccountIdPrefix::version`](crate::account::AccountIdPrefix::version) for details.
107    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    /// See [`AccountIdPrefix::to_hex`](crate::account::AccountIdPrefix::to_hex) for details.
113    pub fn to_hex(self) -> String {
114        format!("0x{:016x}", self.prefix.as_canonical_u64())
115    }
116}
117
118// CONVERSIONS FROM ACCOUNT ID PREFIX
119// ================================================================================================
120
121impl 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
141// CONVERSIONS TO ACCOUNT ID PREFIX
142// ================================================================================================
143
144impl TryFrom<[u8; 8]> for AccountIdPrefixV0 {
145    type Error = AccountIdError;
146
147    /// See [`TryFrom<[u8; 8]> for
148    /// AccountIdPrefix`](crate::account::AccountIdPrefix#impl-TryFrom<%5Bu8;+8%
149    /// 5D>-for-AccountIdPrefix) for details.
150    fn try_from(mut value: [u8; 8]) -> Result<Self, Self::Error> {
151        // Reverse to little-endian order.
152        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    /// See [`TryFrom<u64> for
169    /// AccountIdPrefix`](crate::account::AccountIdPrefix#impl-TryFrom<u64>-for-AccountIdPrefix)
170    /// for details.
171    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    /// See [`TryFrom<Felt> for
185    /// AccountIdPrefix`](crate::account::AccountIdPrefix#impl-TryFrom<Felt>-for-AccountIdPrefix)
186    /// for details.
187    fn try_from(element: Felt) -> Result<Self, Self::Error> {
188        Self::new(element)
189    }
190}
191
192// COMMON TRAIT IMPLS
193// ================================================================================================
194
195impl 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
213// SERIALIZATION
214// ================================================================================================
215
216impl 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// TESTS
236// ================================================================================================
237
238#[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}