miden_objects/address/
address_id.rs

1use alloc::string::ToString;
2
3use bech32::Bech32m;
4use bech32::primitives::decode::CheckedHrpstring;
5use miden_processor::DeserializationError;
6
7use crate::AddressError;
8use crate::account::{AccountId, AccountStorageMode};
9use crate::address::{AddressType, NetworkId};
10use crate::errors::Bech32Error;
11use crate::note::NoteTag;
12use crate::utils::serde::{ByteWriter, Deserializable, Serializable};
13
14/// The identifier of an [`Address`](super::Address).
15///
16/// See the address docs for more details.
17#[non_exhaustive]
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum AddressId {
20    AccountId(AccountId),
21}
22
23impl AddressId {
24    /// Returns the [`AddressType`] of this ID.
25    pub fn address_type(&self) -> AddressType {
26        match self {
27            AddressId::AccountId(_) => AddressType::AccountId,
28        }
29    }
30
31    /// Returns the default tag length of the ID.
32    ///
33    /// This is guaranteed to be in range `0..=30` (e.g. the maximum of
34    /// [`NoteTag::MAX_LOCAL_TAG_LENGTH`] and [`NoteTag::DEFAULT_NETWORK_TAG_LENGTH`]).
35    pub fn default_note_tag_len(&self) -> u8 {
36        match self {
37            AddressId::AccountId(id) => {
38                if id.storage_mode() == AccountStorageMode::Network {
39                    NoteTag::DEFAULT_NETWORK_TAG_LENGTH
40                } else {
41                    NoteTag::DEFAULT_LOCAL_TAG_LENGTH
42                }
43            },
44        }
45    }
46
47    /// Decodes a bech32 string into an identifier.
48    pub(crate) fn decode(bech32_string: &str) -> Result<(NetworkId, Self), AddressError> {
49        // We use CheckedHrpString with an explicit checksum algorithm so we don't allow the
50        // `Bech32` or `NoChecksum` algorithms.
51        let checked_string = CheckedHrpstring::new::<Bech32m>(bech32_string).map_err(|source| {
52            // The CheckedHrpStringError does not implement core::error::Error, only
53            // std::error::Error, so for now we convert it to a String. Even if it will
54            // implement the trait in the future, we should include it as an opaque
55            // error since the crate does not have a stable release yet.
56            AddressError::Bech32DecodeError(Bech32Error::DecodeError(source.to_string().into()))
57        })?;
58
59        let hrp = checked_string.hrp();
60        let network_id = NetworkId::from_hrp(hrp);
61
62        let mut byte_iter = checked_string.byte_iter();
63
64        // We only know the expected length once we know the address type, but to get the
65        // address type, the length must be at least one.
66        let address_byte = byte_iter.next().ok_or_else(|| {
67            AddressError::Bech32DecodeError(Bech32Error::InvalidDataLength {
68                expected: 1,
69                actual: byte_iter.len(),
70            })
71        })?;
72
73        let address_type = AddressType::try_from(address_byte)?;
74
75        let identifier = match address_type {
76            AddressType::AccountId => AccountId::from_bech32_byte_iter(byte_iter)
77                .map_err(AddressError::AccountIdDecodeError)
78                .map(AddressId::AccountId)?,
79        };
80
81        Ok((network_id, identifier))
82    }
83}
84
85impl From<AccountId> for AddressId {
86    fn from(id: AccountId) -> Self {
87        Self::AccountId(id)
88    }
89}
90
91impl Serializable for AddressId {
92    fn write_into<W: ByteWriter>(&self, target: &mut W) {
93        target.write_u8(self.address_type() as u8);
94        match self {
95            AddressId::AccountId(id) => {
96                id.write_into(target);
97            },
98        }
99    }
100}
101
102impl Deserializable for AddressId {
103    fn read_from<R: miden_core::utils::ByteReader>(
104        source: &mut R,
105    ) -> Result<Self, DeserializationError> {
106        let address_type: u8 = source.read_u8()?;
107        let address_type = AddressType::try_from(address_type)
108            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
109
110        match address_type {
111            AddressType::AccountId => {
112                let id: AccountId = source.read()?;
113                Ok(AddressId::AccountId(id))
114            },
115        }
116    }
117}