miden_objects/account/account_id/
id_prefix.rs

1use alloc::string::{String, ToString};
2use core::fmt;
3
4use miden_crypto::utils::ByteWriter;
5use vm_core::{
6    utils::{ByteReader, Deserializable, Serializable},
7    Felt,
8};
9use vm_processor::DeserializationError;
10
11use super::v0;
12use crate::{
13    account::{
14        account_id::AccountIdPrefixV0, AccountIdV0, AccountIdVersion, AccountStorageMode,
15        AccountType,
16    },
17    errors::AccountIdError,
18};
19
20// ACCOUNT ID PREFIX
21// ================================================================================================
22
23/// The prefix of an [`AccountId`][id], i.e. its first field element.
24///
25/// See the [`AccountId`][id] documentation for details.
26///
27/// The serialization formats of [`AccountIdPrefix`] and [`AccountId`][id] are compatible. In
28/// particular, a prefix can be deserialized from the serialized bytes of a full id.
29///
30/// [id]: crate::account::AccountId
31#[derive(Debug, Copy, Clone, Eq, PartialEq)]
32pub enum AccountIdPrefix {
33    V0(AccountIdPrefixV0),
34}
35
36impl AccountIdPrefix {
37    // CONSTANTS
38    // --------------------------------------------------------------------------------------------
39
40    /// The serialized size of an [`AccountIdPrefix`] in bytes.
41    pub const SERIALIZED_SIZE: usize = 8;
42
43    // CONSTRUCTORS
44    // --------------------------------------------------------------------------------------------
45
46    /// Constructs a new [`AccountIdPrefix`] from the given `prefix` without checking its
47    /// validity.
48    ///
49    /// # Warning
50    ///
51    /// Validity of the ID prefix must be ensured by the caller. An invalid ID may lead to panics.
52    ///
53    /// # Panics
54    ///
55    /// Panics if the prefix does not contain a known account ID version.
56    ///
57    /// If debug_assertions are enabled (e.g. in debug mode), this function panics if the given
58    /// felt is invalid according to the constraints in the
59    /// [`AccountId`](crate::account::AccountId) documentation.
60    pub fn new_unchecked(prefix: Felt) -> Self {
61        // The prefix contains the metadata.
62        // If we add more versions in the future, we may need to generalize this.
63        match v0::extract_version(prefix.as_int())
64            .expect("prefix should contain a valid account ID version")
65        {
66            AccountIdVersion::Version0 => Self::V0(AccountIdPrefixV0::new_unchecked(prefix)),
67        }
68    }
69
70    /// Constructs a new [`AccountIdPrefix`] from the given `prefix` and checks its validity.
71    ///
72    /// # Errors
73    ///
74    /// Returns an error if any of the ID constraints are not met. See the [constraints
75    /// documentation](super::AccountId#constraints) for details.
76    pub fn new(prefix: Felt) -> Result<Self, AccountIdError> {
77        // The prefix contains the metadata.
78        // If we add more versions in the future, we may need to generalize this.
79        match v0::extract_version(prefix.as_int())? {
80            AccountIdVersion::Version0 => AccountIdPrefixV0::new(prefix).map(Self::V0),
81        }
82    }
83
84    // PUBLIC ACCESSORS
85    // --------------------------------------------------------------------------------------------
86
87    /// Returns the [`Felt`] that represents this prefix.
88    pub const fn as_felt(&self) -> Felt {
89        match self {
90            AccountIdPrefix::V0(id_prefix) => id_prefix.as_felt(),
91        }
92    }
93
94    /// Returns the prefix as a [`u64`].
95    pub const fn as_u64(&self) -> u64 {
96        match self {
97            AccountIdPrefix::V0(id_prefix) => id_prefix.as_u64(),
98        }
99    }
100
101    /// Returns the type of this account ID.
102    pub const fn account_type(&self) -> AccountType {
103        match self {
104            AccountIdPrefix::V0(id_prefix) => id_prefix.account_type(),
105        }
106    }
107
108    /// Returns true if an account with this ID is a faucet (can issue assets).
109    pub fn is_faucet(&self) -> bool {
110        self.account_type().is_faucet()
111    }
112
113    /// Returns true if an account with this ID is a regular account.
114    pub fn is_regular_account(&self) -> bool {
115        self.account_type().is_regular_account()
116    }
117
118    /// Returns the storage mode of this account ID.
119    pub fn storage_mode(&self) -> AccountStorageMode {
120        match self {
121            AccountIdPrefix::V0(id_prefix) => id_prefix.storage_mode(),
122        }
123    }
124
125    /// Returns true if an account with this ID is a public account.
126    pub fn is_public(&self) -> bool {
127        self.storage_mode() == AccountStorageMode::Public
128    }
129
130    /// Returns the version of this account ID.
131    pub fn version(&self) -> AccountIdVersion {
132        match self {
133            AccountIdPrefix::V0(_) => AccountIdVersion::Version0,
134        }
135    }
136
137    /// Returns the prefix as a big-endian, hex-encoded string.
138    pub fn to_hex(self) -> String {
139        match self {
140            AccountIdPrefix::V0(id_prefix) => id_prefix.to_hex(),
141        }
142    }
143
144    /// Returns `felt` with the fungible bit set to zero. The version must be passed as the location
145    /// of the fungible bit may depend on the underlying account ID version.
146    pub(crate) fn clear_fungible_bit(version: AccountIdVersion, felt: Felt) -> Felt {
147        match version {
148            AccountIdVersion::Version0 => {
149                // Set the fungible bit to zero by taking the bitwise `and` of the felt with the
150                // inverted is_faucet mask.
151                let clear_fungible_bit_mask = !AccountIdV0::IS_FAUCET_MASK;
152                Felt::try_from(felt.as_int() & clear_fungible_bit_mask)
153                    .expect("felt should still be valid as we cleared a bit and did not set any")
154            },
155        }
156    }
157}
158
159// CONVERSIONS FROM ACCOUNT ID PREFIX
160// ================================================================================================
161
162impl From<AccountIdPrefixV0> for AccountIdPrefix {
163    fn from(id: AccountIdPrefixV0) -> Self {
164        Self::V0(id)
165    }
166}
167
168impl From<AccountIdPrefix> for Felt {
169    fn from(id: AccountIdPrefix) -> Self {
170        match id {
171            AccountIdPrefix::V0(id_prefix) => id_prefix.into(),
172        }
173    }
174}
175
176impl From<AccountIdPrefix> for [u8; 8] {
177    fn from(id: AccountIdPrefix) -> Self {
178        match id {
179            AccountIdPrefix::V0(id_prefix) => id_prefix.into(),
180        }
181    }
182}
183
184impl From<AccountIdPrefix> for u64 {
185    fn from(id: AccountIdPrefix) -> Self {
186        match id {
187            AccountIdPrefix::V0(id_prefix) => id_prefix.into(),
188        }
189    }
190}
191
192// CONVERSIONS TO ACCOUNT ID PREFIX
193// ================================================================================================
194
195impl TryFrom<[u8; 8]> for AccountIdPrefix {
196    type Error = AccountIdError;
197
198    /// Tries to convert a byte array in big-endian order to an [`AccountIdPrefix`].
199    ///
200    /// # Errors
201    ///
202    /// Returns an error if any of the ID constraints are not met. See the [constraints
203    /// documentation](super::AccountId#constraints) for details.
204    fn try_from(value: [u8; 8]) -> Result<Self, Self::Error> {
205        // The least significant byte of the ID prefix contains the metadata.
206        let metadata_byte = value[7];
207        // We only have one supported version for now, so we use the extractor from that version.
208        // If we add more versions in the future, we may need to generalize this.
209        let version = v0::extract_version(metadata_byte as u64)?;
210
211        match version {
212            AccountIdVersion::Version0 => AccountIdPrefixV0::try_from(value).map(Self::V0),
213        }
214    }
215}
216
217impl TryFrom<u64> for AccountIdPrefix {
218    type Error = AccountIdError;
219
220    /// Tries to convert a `u64` into an [`AccountIdPrefix`].
221    ///
222    /// # Errors
223    ///
224    /// Returns an error if any of the ID constraints are not met. See the [constraints
225    /// documentation](super::AccountId#constraints) for details.
226    fn try_from(value: u64) -> Result<Self, Self::Error> {
227        let element = Felt::try_from(value.to_le_bytes().as_slice())
228            .map_err(AccountIdError::AccountIdInvalidPrefixFieldElement)?;
229        Self::new(element)
230    }
231}
232
233impl TryFrom<Felt> for AccountIdPrefix {
234    type Error = AccountIdError;
235
236    /// Returns an [`AccountIdPrefix`] instantiated with the provided field element.
237    ///
238    /// # Errors
239    ///
240    /// Returns an error if any of the ID constraints are not met. See the [constraints
241    /// documentation](super::AccountId#constraints) for details.
242    fn try_from(element: Felt) -> Result<Self, Self::Error> {
243        Self::new(element)
244    }
245}
246
247// COMMON TRAIT IMPLS
248// ================================================================================================
249
250impl PartialOrd for AccountIdPrefix {
251    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
252        Some(self.cmp(other))
253    }
254}
255
256impl Ord for AccountIdPrefix {
257    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
258        u64::from(*self).cmp(&u64::from(*other))
259    }
260}
261
262impl fmt::Display for AccountIdPrefix {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        write!(f, "{}", self.to_hex())
265    }
266}
267
268// SERIALIZATION
269// ================================================================================================
270
271impl Serializable for AccountIdPrefix {
272    fn write_into<W: ByteWriter>(&self, target: &mut W) {
273        match self {
274            AccountIdPrefix::V0(id_prefix) => id_prefix.write_into(target),
275        }
276    }
277
278    fn get_size_hint(&self) -> usize {
279        match self {
280            AccountIdPrefix::V0(id_prefix) => id_prefix.get_size_hint(),
281        }
282    }
283}
284
285impl Deserializable for AccountIdPrefix {
286    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
287        <[u8; 8]>::read_from(source)?
288            .try_into()
289            .map_err(|err: AccountIdError| DeserializationError::InvalidValue(err.to_string()))
290    }
291}
292
293// TESTS
294// ================================================================================================
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299    use crate::account::AccountIdV0;
300
301    #[test]
302    fn account_id_prefix_construction() {
303        // Use the highest possible input to check if the constructed id is a valid Felt in that
304        // scenario.
305        // Use the lowest possible input to check whether the constructor produces valid IDs with
306        // all-zeroes input.
307        for input in [[0xff; 15], [0; 15]] {
308            for account_type in [
309                AccountType::FungibleFaucet,
310                AccountType::NonFungibleFaucet,
311                AccountType::RegularAccountImmutableCode,
312                AccountType::RegularAccountUpdatableCode,
313            ] {
314                for storage_mode in [AccountStorageMode::Private, AccountStorageMode::Public] {
315                    let id = AccountIdV0::dummy(input, account_type, storage_mode);
316                    let prefix = id.prefix();
317                    assert_eq!(prefix.account_type(), account_type);
318                    assert_eq!(prefix.storage_mode(), storage_mode);
319                    assert_eq!(prefix.version(), AccountIdVersion::Version0);
320
321                    // Do a serialization roundtrip to ensure validity.
322                    let serialized_prefix = prefix.to_bytes();
323                    AccountIdPrefix::read_from_bytes(&serialized_prefix).unwrap();
324                    assert_eq!(serialized_prefix.len(), AccountIdPrefix::SERIALIZED_SIZE);
325                }
326            }
327        }
328    }
329}