miden_objects/account/account_id/v0/
prefix.rs1use 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 crate::{
12 account::{
13 account_id::v0::{self, validate_prefix},
14 AccountIdVersion, AccountStorageMode, AccountType,
15 },
16 errors::AccountIdError,
17};
18
19#[derive(Debug, Copy, Clone, Eq, PartialEq)]
26pub struct AccountIdPrefixV0 {
27 prefix: Felt,
28}
29
30impl AccountIdPrefixV0 {
31 const SERIALIZED_SIZE: usize = 8;
36
37 pub fn new_unchecked(prefix: Felt) -> Self {
43 if cfg!(debug_assertions) {
45 validate_prefix(prefix)
46 .expect("AccountIdPrefix::new_unchecked called with invalid prefix");
47 }
48
49 AccountIdPrefixV0 { prefix }
50 }
51
52 pub fn new(prefix: Felt) -> Result<Self, AccountIdError> {
54 validate_prefix(prefix)?;
55
56 Ok(AccountIdPrefixV0 { prefix })
57 }
58
59 pub const fn as_felt(&self) -> Felt {
64 self.prefix
65 }
66
67 pub const fn as_u64(&self) -> u64 {
69 self.prefix.as_int()
70 }
71
72 pub const fn account_type(&self) -> AccountType {
75 v0::extract_type(self.prefix.as_int())
76 }
77
78 pub fn is_faucet(&self) -> bool {
80 self.account_type().is_faucet()
81 }
82
83 pub fn is_regular_account(&self) -> bool {
86 self.account_type().is_regular_account()
87 }
88
89 pub fn storage_mode(&self) -> AccountStorageMode {
92 v0::extract_storage_mode(self.prefix.as_int())
93 .expect("account ID prefix should have been constructed with a valid storage mode")
94 }
95
96 pub fn is_public(&self) -> bool {
98 self.storage_mode() == AccountStorageMode::Public
99 }
100
101 pub fn version(&self) -> AccountIdVersion {
103 v0::extract_version(self.prefix.as_int())
104 .expect("account ID prefix should have been constructed with a valid version")
105 }
106
107 pub fn to_hex(self) -> String {
109 format!("0x{:016x}", self.prefix.as_int())
110 }
111}
112
113impl From<AccountIdPrefixV0> for Felt {
117 fn from(id: AccountIdPrefixV0) -> Self {
118 id.prefix
119 }
120}
121
122impl From<AccountIdPrefixV0> for [u8; 8] {
123 fn from(id: AccountIdPrefixV0) -> Self {
124 let mut result = [0_u8; 8];
125 result[..8].copy_from_slice(&id.prefix.as_int().to_be_bytes());
126 result
127 }
128}
129
130impl From<AccountIdPrefixV0> for u64 {
131 fn from(id: AccountIdPrefixV0) -> Self {
132 id.prefix.as_int()
133 }
134}
135
136impl TryFrom<[u8; 8]> for AccountIdPrefixV0 {
140 type Error = AccountIdError;
141
142 fn try_from(mut value: [u8; 8]) -> Result<Self, Self::Error> {
146 value.reverse();
148
149 Felt::try_from(value.as_slice())
150 .map_err(AccountIdError::AccountIdInvalidPrefixFieldElement)
151 .and_then(Self::new)
152 }
153}
154
155impl TryFrom<u64> for AccountIdPrefixV0 {
156 type Error = AccountIdError;
157
158 fn try_from(value: u64) -> Result<Self, Self::Error> {
162 let element = Felt::try_from(value.to_le_bytes().as_slice())
163 .map_err(AccountIdError::AccountIdInvalidPrefixFieldElement)?;
164 Self::new(element)
165 }
166}
167
168impl TryFrom<Felt> for AccountIdPrefixV0 {
169 type Error = AccountIdError;
170
171 fn try_from(element: Felt) -> Result<Self, Self::Error> {
175 Self::new(element)
176 }
177}
178
179impl PartialOrd for AccountIdPrefixV0 {
183 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
184 Some(self.cmp(other))
185 }
186}
187
188impl Ord for AccountIdPrefixV0 {
189 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
190 self.prefix.as_int().cmp(&other.prefix.as_int())
191 }
192}
193
194impl fmt::Display for AccountIdPrefixV0 {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 write!(f, "{}", self.to_hex())
197 }
198}
199
200impl Serializable for AccountIdPrefixV0 {
204 fn write_into<W: ByteWriter>(&self, target: &mut W) {
205 let bytes: [u8; 8] = (*self).into();
206 bytes.write_into(target);
207 }
208
209 fn get_size_hint(&self) -> usize {
210 Self::SERIALIZED_SIZE
211 }
212}
213
214impl Deserializable for AccountIdPrefixV0 {
215 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
216 <[u8; 8]>::read_from(source)?
217 .try_into()
218 .map_err(|err: AccountIdError| DeserializationError::InvalidValue(err.to_string()))
219 }
220}
221
222#[cfg(test)]
226mod tests {
227 use super::*;
228 use crate::{
229 account::{AccountId, AccountIdPrefix},
230 testing::account_id::{
231 ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN, ACCOUNT_ID_NON_FUNGIBLE_FAUCET_OFF_CHAIN,
232 ACCOUNT_ID_OFF_CHAIN_SENDER, ACCOUNT_ID_REGULAR_ACCOUNT_IMMUTABLE_CODE_ON_CHAIN,
233 ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN,
234 },
235 };
236
237 #[test]
238 fn test_account_id_prefix_conversion_roundtrip() {
239 for (idx, account_id) in [
240 ACCOUNT_ID_REGULAR_ACCOUNT_IMMUTABLE_CODE_ON_CHAIN,
241 ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN,
242 ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN,
243 ACCOUNT_ID_NON_FUNGIBLE_FAUCET_OFF_CHAIN,
244 ACCOUNT_ID_OFF_CHAIN_SENDER,
245 ]
246 .into_iter()
247 .enumerate()
248 {
249 let full_id = AccountId::try_from(account_id).unwrap();
250 let prefix = full_id.prefix();
251 assert_eq!(
252 prefix,
253 AccountIdPrefix::read_from_bytes(&prefix.to_bytes()).unwrap(),
254 "failed in {idx}"
255 );
256 }
257 }
258}