miden_objects/account/account_id/v0/
prefix.rs

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