Skip to main content

miden_protocol/account/account_id/v1/
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::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// ACCOUNT ID PREFIX VERSION 1
19// ================================================================================================
20
21/// The prefix of an [`AccountIdV1`](crate::account::AccountIdV1), 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 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    // CONSTANTS
37    // --------------------------------------------------------------------------------------------
38
39    /// The serialized size of an [`AccountIdPrefixV1`] 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        AccountIdPrefixV1 { 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(AccountIdPrefixV1 { 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        v1::extract_account_type(self.prefix.as_canonical_u64())
81    }
82
83    /// See [`AccountIdPrefix::is_public`](crate::account::AccountIdPrefix::is_public) for details.
84    pub fn is_public(&self) -> bool {
85        self.account_type().is_public()
86    }
87
88    /// See [`AccountIdPrefix::version`](crate::account::AccountIdPrefix::version) for details.
89    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    /// See [`AccountIdPrefix::to_hex`](crate::account::AccountIdPrefix::to_hex) for details.
95    pub fn to_hex(self) -> String {
96        format!("0x{:016x}", self.prefix.as_canonical_u64())
97    }
98}
99
100// CONVERSIONS FROM ACCOUNT ID PREFIX
101// ================================================================================================
102
103impl 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
123// CONVERSIONS TO ACCOUNT ID PREFIX
124// ================================================================================================
125
126impl TryFrom<[u8; 8]> for AccountIdPrefixV1 {
127    type Error = AccountIdError;
128
129    /// See [`TryFrom<[u8; 8]> for
130    /// AccountIdPrefix`](crate::account::AccountIdPrefix#impl-TryFrom<%5Bu8;+8%
131    /// 5D>-for-AccountIdPrefix) for details.
132    fn try_from(mut value: [u8; 8]) -> Result<Self, Self::Error> {
133        // Reverse to little-endian order.
134        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    /// See [`TryFrom<u64> for
151    /// AccountIdPrefix`](crate::account::AccountIdPrefix#impl-TryFrom<u64>-for-AccountIdPrefix)
152    /// for details.
153    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    /// See [`TryFrom<Felt> for
167    /// AccountIdPrefix`](crate::account::AccountIdPrefix#impl-TryFrom<Felt>-for-AccountIdPrefix)
168    /// for details.
169    fn try_from(element: Felt) -> Result<Self, Self::Error> {
170        Self::new(element)
171    }
172}
173
174// COMMON TRAIT IMPLS
175// ================================================================================================
176
177impl 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
195// SERIALIZATION
196// ================================================================================================
197
198impl 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// TESTS
218// ================================================================================================
219
220#[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}