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}