Skip to main content

miden_protocol/account/account_id/
id_prefix.rs

1use alloc::string::{String, ToString};
2use core::fmt;
3
4use super::v1;
5use crate::Felt;
6use crate::account::account_id::AccountIdPrefixV1;
7use crate::account::{AccountIdVersion, AccountType};
8use crate::errors::AccountIdError;
9use crate::utils::serde::{
10    ByteReader,
11    ByteWriter,
12    Deserializable,
13    DeserializationError,
14    Serializable,
15};
16
17// ACCOUNT ID PREFIX
18// ================================================================================================
19
20/// The prefix of an [`AccountId`][id], i.e. its first field element.
21///
22/// See the [`AccountId`][id] documentation for details.
23///
24/// The serialization formats of [`AccountIdPrefix`] and [`AccountId`][id] are compatible. In
25/// particular, a prefix can be deserialized from the serialized bytes of a full id.
26///
27/// [id]: crate::account::AccountId
28#[derive(Debug, Copy, Clone, Eq, PartialEq)]
29pub enum AccountIdPrefix {
30    V1(AccountIdPrefixV1),
31}
32
33impl AccountIdPrefix {
34    // CONSTANTS
35    // --------------------------------------------------------------------------------------------
36
37    /// The serialized size of an [`AccountIdPrefix`] in bytes.
38    pub const SERIALIZED_SIZE: usize = 8;
39
40    // CONSTRUCTORS
41    // --------------------------------------------------------------------------------------------
42
43    /// Constructs a new [`AccountIdPrefix`] from the given `prefix` without checking its
44    /// validity.
45    ///
46    /// # Warning
47    ///
48    /// Validity of the ID prefix must be ensured by the caller. An invalid ID may lead to panics.
49    ///
50    /// # Panics
51    ///
52    /// Panics if the prefix does not contain a known account ID version.
53    ///
54    /// If `debug_assertions` are enabled (e.g. in debug mode), this function panics if the given
55    /// felt is invalid according to the constraints in the [`AccountId`](crate::account::AccountId)
56    /// documentation.
57    pub fn new_unchecked(prefix: Felt) -> Self {
58        // The prefix contains the metadata.
59        // If we add more versions in the future, we may need to generalize this.
60        match v1::extract_version(prefix.as_canonical_u64())
61            .expect("prefix should contain a valid account ID version")
62        {
63            AccountIdVersion::Version1 => Self::V1(AccountIdPrefixV1::new_unchecked(prefix)),
64        }
65    }
66
67    /// Constructs a new [`AccountIdPrefix`] from the given `prefix` and checks its validity.
68    ///
69    /// # Errors
70    ///
71    /// Returns an error if any of the ID constraints are not met. See the [constraints
72    /// documentation](super::AccountId#constraints) for details.
73    pub fn new(prefix: Felt) -> Result<Self, AccountIdError> {
74        // The prefix contains the metadata.
75        // If we add more versions in the future, we may need to generalize this.
76        match v1::extract_version(prefix.as_canonical_u64())? {
77            AccountIdVersion::Version1 => AccountIdPrefixV1::new(prefix).map(Self::V1),
78        }
79    }
80
81    // PUBLIC ACCESSORS
82    // --------------------------------------------------------------------------------------------
83
84    /// Returns the [`Felt`] that represents this prefix.
85    pub const fn as_felt(&self) -> Felt {
86        match self {
87            AccountIdPrefix::V1(id_prefix) => id_prefix.as_felt(),
88        }
89    }
90
91    /// Returns the prefix as a [`u64`].
92    pub fn as_u64(&self) -> u64 {
93        match self {
94            AccountIdPrefix::V1(id_prefix) => id_prefix.as_u64(),
95        }
96    }
97
98    /// Returns the account type of this account ID.
99    pub fn account_type(&self) -> AccountType {
100        match self {
101            AccountIdPrefix::V1(id_prefix) => id_prefix.account_type(),
102        }
103    }
104
105    /// Returns `true` if the account type is [`AccountType::Public`], `false` otherwise.
106    pub fn is_public(&self) -> bool {
107        self.account_type().is_public()
108    }
109
110    /// Returns `true` if self is a private account, `false` otherwise.
111    pub fn is_private(&self) -> bool {
112        self.account_type().is_private()
113    }
114
115    /// Returns the version of this account ID.
116    pub fn version(&self) -> AccountIdVersion {
117        match self {
118            AccountIdPrefix::V1(_) => AccountIdVersion::Version1,
119        }
120    }
121
122    /// Returns the prefix as a big-endian, hex-encoded string.
123    pub fn to_hex(self) -> String {
124        match self {
125            AccountIdPrefix::V1(id_prefix) => id_prefix.to_hex(),
126        }
127    }
128}
129
130// CONVERSIONS FROM ACCOUNT ID PREFIX
131// ================================================================================================
132
133impl From<AccountIdPrefixV1> for AccountIdPrefix {
134    fn from(id: AccountIdPrefixV1) -> Self {
135        Self::V1(id)
136    }
137}
138
139impl From<AccountIdPrefix> for Felt {
140    fn from(id: AccountIdPrefix) -> Self {
141        match id {
142            AccountIdPrefix::V1(id_prefix) => id_prefix.into(),
143        }
144    }
145}
146
147impl From<AccountIdPrefix> for [u8; 8] {
148    fn from(id: AccountIdPrefix) -> Self {
149        match id {
150            AccountIdPrefix::V1(id_prefix) => id_prefix.into(),
151        }
152    }
153}
154
155impl From<AccountIdPrefix> for u64 {
156    fn from(id: AccountIdPrefix) -> Self {
157        match id {
158            AccountIdPrefix::V1(id_prefix) => id_prefix.into(),
159        }
160    }
161}
162
163// CONVERSIONS TO ACCOUNT ID PREFIX
164// ================================================================================================
165
166impl TryFrom<[u8; 8]> for AccountIdPrefix {
167    type Error = AccountIdError;
168
169    /// Tries to convert a byte array in big-endian order to an [`AccountIdPrefix`].
170    ///
171    /// # Errors
172    ///
173    /// Returns an error if any of the ID constraints are not met. See the [constraints
174    /// documentation](super::AccountId#constraints) for details.
175    fn try_from(value: [u8; 8]) -> Result<Self, Self::Error> {
176        // The least significant byte of the ID prefix contains the metadata.
177        let metadata_byte = value[7];
178        // We only have one supported version for now, so we use the extractor from that version.
179        // If we add more versions in the future, we may need to generalize this.
180        let version = v1::extract_version(metadata_byte as u64)?;
181
182        match version {
183            AccountIdVersion::Version1 => AccountIdPrefixV1::try_from(value).map(Self::V1),
184        }
185    }
186}
187
188impl TryFrom<u64> for AccountIdPrefix {
189    type Error = AccountIdError;
190
191    /// Tries to convert a `u64` into an [`AccountIdPrefix`].
192    ///
193    /// # Errors
194    ///
195    /// Returns an error if any of the ID constraints are not met. See the [constraints
196    /// documentation](super::AccountId#constraints) for details.
197    fn try_from(value: u64) -> Result<Self, Self::Error> {
198        let element = Felt::try_from(value).map_err(|err| {
199            AccountIdError::AccountIdInvalidPrefixFieldElement(DeserializationError::InvalidValue(
200                err.to_string(),
201            ))
202        })?;
203        Self::new(element)
204    }
205}
206
207impl TryFrom<Felt> for AccountIdPrefix {
208    type Error = AccountIdError;
209
210    /// Returns an [`AccountIdPrefix`] instantiated with the provided field element.
211    ///
212    /// # Errors
213    ///
214    /// Returns an error if any of the ID constraints are not met. See the [constraints
215    /// documentation](super::AccountId#constraints) for details.
216    fn try_from(element: Felt) -> Result<Self, Self::Error> {
217        Self::new(element)
218    }
219}
220
221// COMMON TRAIT IMPLS
222// ================================================================================================
223
224impl PartialOrd for AccountIdPrefix {
225    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
226        Some(self.cmp(other))
227    }
228}
229
230impl Ord for AccountIdPrefix {
231    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
232        u64::from(*self).cmp(&u64::from(*other))
233    }
234}
235
236impl fmt::Display for AccountIdPrefix {
237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238        write!(f, "{}", self.to_hex())
239    }
240}
241
242// SERIALIZATION
243// ================================================================================================
244
245impl Serializable for AccountIdPrefix {
246    fn write_into<W: ByteWriter>(&self, target: &mut W) {
247        match self {
248            AccountIdPrefix::V1(id_prefix) => id_prefix.write_into(target),
249        }
250    }
251
252    fn get_size_hint(&self) -> usize {
253        match self {
254            AccountIdPrefix::V1(id_prefix) => id_prefix.get_size_hint(),
255        }
256    }
257}
258
259impl Deserializable for AccountIdPrefix {
260    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
261        <[u8; 8]>::read_from(source)?
262            .try_into()
263            .map_err(|err: AccountIdError| DeserializationError::InvalidValue(err.to_string()))
264    }
265}