Skip to main content

miden_protocol/address/
address_id.rs

1use alloc::string::ToString;
2
3use bech32::Bech32m;
4use bech32::primitives::decode::CheckedHrpstring;
5use miden_processor::DeserializationError;
6
7use crate::account::{AccountId, AccountStorageMode};
8use crate::address::{AddressType, NetworkId};
9use crate::errors::{AddressError, Bech32Error};
10use crate::note::NoteTag;
11use crate::utils::serde::{ByteWriter, Deserializable, Serializable};
12
13/// The identifier of an [`Address`](super::Address).
14///
15/// See the address docs for more details.
16#[non_exhaustive]
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum AddressId {
19    AccountId(AccountId),
20}
21
22impl AddressId {
23    /// Returns the [`AddressType`] of this ID.
24    pub fn address_type(&self) -> AddressType {
25        match self {
26            AddressId::AccountId(_) => AddressType::AccountId,
27        }
28    }
29
30    /// Returns the default tag length of the ID.
31    ///
32    /// This is guaranteed to be in range `0..=30` (e.g. the maximum of
33    /// [`NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH`] and
34    /// [`NoteTag::DEFAULT_NETWORK_ACCOUNT_TARGET_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_ACCOUNT_TARGET_TAG_LENGTH
40                } else {
41                    NoteTag::DEFAULT_LOCAL_ACCOUNT_TARGET_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}