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, AssetCallbackFlag};
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 the [`AssetCallbackFlag`] of this account ID prefix.
106 ///
107 /// See [`AccountId::asset_callback_flag`](crate::account::AccountId::asset_callback_flag) for
108 /// details.
109 pub fn asset_callback_flag(&self) -> AssetCallbackFlag {
110 match self {
111 AccountIdPrefix::V1(id_prefix) => id_prefix.asset_callback_flag(),
112 }
113 }
114
115 /// Returns `true` if the account type is [`AccountType::Public`], `false` otherwise.
116 pub fn is_public(&self) -> bool {
117 self.account_type().is_public()
118 }
119
120 /// Returns `true` if self is a private account, `false` otherwise.
121 pub fn is_private(&self) -> bool {
122 self.account_type().is_private()
123 }
124
125 /// Returns the version of this account ID.
126 pub fn version(&self) -> AccountIdVersion {
127 match self {
128 AccountIdPrefix::V1(_) => AccountIdVersion::Version1,
129 }
130 }
131
132 /// Returns the prefix as a big-endian, hex-encoded string.
133 pub fn to_hex(self) -> String {
134 match self {
135 AccountIdPrefix::V1(id_prefix) => id_prefix.to_hex(),
136 }
137 }
138}
139
140// CONVERSIONS FROM ACCOUNT ID PREFIX
141// ================================================================================================
142
143impl From<AccountIdPrefixV1> for AccountIdPrefix {
144 fn from(id: AccountIdPrefixV1) -> Self {
145 Self::V1(id)
146 }
147}
148
149impl From<AccountIdPrefix> for Felt {
150 fn from(id: AccountIdPrefix) -> Self {
151 match id {
152 AccountIdPrefix::V1(id_prefix) => id_prefix.into(),
153 }
154 }
155}
156
157impl From<AccountIdPrefix> for [u8; 8] {
158 fn from(id: AccountIdPrefix) -> Self {
159 match id {
160 AccountIdPrefix::V1(id_prefix) => id_prefix.into(),
161 }
162 }
163}
164
165impl From<AccountIdPrefix> for u64 {
166 fn from(id: AccountIdPrefix) -> Self {
167 match id {
168 AccountIdPrefix::V1(id_prefix) => id_prefix.into(),
169 }
170 }
171}
172
173// CONVERSIONS TO ACCOUNT ID PREFIX
174// ================================================================================================
175
176impl TryFrom<[u8; 8]> for AccountIdPrefix {
177 type Error = AccountIdError;
178
179 /// Tries to convert a byte array in big-endian order to an [`AccountIdPrefix`].
180 ///
181 /// # Errors
182 ///
183 /// Returns an error if any of the ID constraints are not met. See the [constraints
184 /// documentation](super::AccountId#constraints) for details.
185 fn try_from(value: [u8; 8]) -> Result<Self, Self::Error> {
186 // The least significant byte of the ID prefix contains the metadata.
187 let metadata_byte = value[7];
188 // We only have one supported version for now, so we use the extractor from that version.
189 // If we add more versions in the future, we may need to generalize this.
190 let version = v1::extract_version(metadata_byte as u64)?;
191
192 match version {
193 AccountIdVersion::Version1 => AccountIdPrefixV1::try_from(value).map(Self::V1),
194 }
195 }
196}
197
198impl TryFrom<u64> for AccountIdPrefix {
199 type Error = AccountIdError;
200
201 /// Tries to convert a `u64` into an [`AccountIdPrefix`].
202 ///
203 /// # Errors
204 ///
205 /// Returns an error if any of the ID constraints are not met. See the [constraints
206 /// documentation](super::AccountId#constraints) for details.
207 fn try_from(value: u64) -> Result<Self, Self::Error> {
208 let element = Felt::try_from(value).map_err(|err| {
209 AccountIdError::AccountIdInvalidPrefixFieldElement(DeserializationError::InvalidValue(
210 err.to_string(),
211 ))
212 })?;
213 Self::new(element)
214 }
215}
216
217impl TryFrom<Felt> for AccountIdPrefix {
218 type Error = AccountIdError;
219
220 /// Returns an [`AccountIdPrefix`] instantiated with the provided field element.
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(element: Felt) -> Result<Self, Self::Error> {
227 Self::new(element)
228 }
229}
230
231// COMMON TRAIT IMPLS
232// ================================================================================================
233
234impl PartialOrd for AccountIdPrefix {
235 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
236 Some(self.cmp(other))
237 }
238}
239
240impl Ord for AccountIdPrefix {
241 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
242 u64::from(*self).cmp(&u64::from(*other))
243 }
244}
245
246impl fmt::Display for AccountIdPrefix {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 write!(f, "{}", self.to_hex())
249 }
250}
251
252// SERIALIZATION
253// ================================================================================================
254
255impl Serializable for AccountIdPrefix {
256 fn write_into<W: ByteWriter>(&self, target: &mut W) {
257 match self {
258 AccountIdPrefix::V1(id_prefix) => id_prefix.write_into(target),
259 }
260 }
261
262 fn get_size_hint(&self) -> usize {
263 match self {
264 AccountIdPrefix::V1(id_prefix) => id_prefix.get_size_hint(),
265 }
266 }
267}
268
269impl Deserializable for AccountIdPrefix {
270 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
271 <[u8; 8]>::read_from(source)?
272 .try_into()
273 .map_err(|err: AccountIdError| DeserializationError::InvalidValue(err.to_string()))
274 }
275}