Skip to main content

snarkvm_console_network/helpers/
id.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::prelude::*;
17
18use anyhow::Result;
19use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
20use std::borrow::Borrow;
21
22pub trait Bech32ID<F: FieldTrait>:
23    From<F>
24    + Deref<Target = F>
25    + Into<Vec<F>>
26    + Uniform
27    + Copy
28    + Clone
29    + Default
30    + Debug
31    + Display
32    + FromStr
33    + ToBytes
34    + FromBytes
35    + Serialize
36    + DeserializeOwned
37    + PartialEq
38    + Eq
39    + core::hash::Hash
40    + Sync
41    + Send
42{
43    fn prefix() -> String;
44    fn size_in_bytes() -> usize;
45    fn number_of_data_characters() -> usize;
46}
47
48#[rustfmt::skip]
49#[macro_export]
50macro_rules! const_assert {
51    ($x:expr $(,)*) => {
52        pub const ASSERT: [(); 1] = [()];
53        pub const fn bool_assert(x: bool) -> bool { x }
54        let _ = ASSERT[!bool_assert($x) as usize];
55    };
56}
57
58/// Converts a string of 2 characters into a `u16` for a human-readable prefix in Bech32.
59#[macro_export]
60macro_rules! hrp2 {
61    ( $persona: expr ) => {{
62        const_assert!($persona.len() == 2);
63        let p = $persona.as_bytes();
64        u16::from_le_bytes([p[0], p[1]])
65    }};
66}
67
68#[derive(Copy, Clone, PartialEq, Eq, Hash)]
69pub struct AleoID<F: FieldTrait, const PREFIX: u16>(F);
70
71impl<F: FieldTrait, const PREFIX: u16> Bech32ID<F> for AleoID<F, PREFIX> {
72    #[inline]
73    fn prefix() -> String {
74        String::from_utf8(PREFIX.to_le_bytes().to_vec()).expect("Failed to convert prefix to string")
75    }
76
77    #[inline]
78    fn size_in_bytes() -> usize {
79        F::size_in_bits().div_ceil(8)
80    }
81
82    #[inline]
83    fn number_of_data_characters() -> usize {
84        (Self::size_in_bytes() * 8).div_ceil(5)
85    }
86}
87
88impl<F: FieldTrait, const PREFIX: u16> From<F> for AleoID<F, PREFIX> {
89    #[inline]
90    fn from(data: F) -> Self {
91        Self(data)
92    }
93}
94
95impl<F: FieldTrait, const PREFIX: u16> Default for AleoID<F, PREFIX> {
96    #[inline]
97    fn default() -> Self {
98        Self(F::zero())
99    }
100}
101
102impl<F: FieldTrait, const PREFIX: u16> FromBytes for AleoID<F, PREFIX> {
103    /// Reads data into a buffer.
104    #[inline]
105    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
106        Ok(Self(F::read_le(&mut reader)?))
107    }
108}
109
110impl<F: FieldTrait, const PREFIX: u16> ToBytes for AleoID<F, PREFIX> {
111    /// Writes the data to a buffer.
112    #[inline]
113    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
114        self.0.write_le(&mut writer)
115    }
116}
117
118impl<F: FieldTrait, const PREFIX: u16> FromStr for AleoID<F, PREFIX> {
119    type Err = Error;
120
121    /// Reads in a bech32m string.
122    #[inline]
123    fn from_str(string: &str) -> Result<Self, Self::Err> {
124        const CHECKSUM_STRING_LENGTH: usize = 6;
125        if string.len() != 3 + Self::number_of_data_characters() + CHECKSUM_STRING_LENGTH {
126            bail!("Invalid byte size for a bech32m hash: {} bytes", string.len())
127        }
128
129        let checked = bech32::primitives::decode::CheckedHrpstring::new::<LongBech32m>(string)?;
130        let hrp = checked.hrp();
131        let data: Vec<u8> = checked.byte_iter().collect();
132        if hrp.as_bytes() != PREFIX.to_le_bytes() {
133            bail!("Invalid prefix for a bech32m hash: {hrp}")
134        };
135        if data.is_empty() {
136            bail!("Bech32m hash data is empty")
137        }
138        Ok(Self::read_le(&*data)?)
139    }
140}
141
142impl<F: FieldTrait, const PREFIX: u16> Display for AleoID<F, PREFIX> {
143    #[inline]
144    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
145        bech32::encode_to_fmt::<LongBech32m, _>(
146            f,
147            bech32::Hrp::parse_unchecked(&Self::prefix()),
148            &self.0.to_bytes_le().expect("Failed to write data as bytes"),
149        )
150        .map_err(|_| fmt::Error)
151    }
152}
153
154impl<F: FieldTrait, const PREFIX: u16> Debug for AleoID<F, PREFIX> {
155    #[inline]
156    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
157        write!(f, "AleoLocator {{ hrp: {:?}, data: {:?} }}", &Self::prefix(), self.0)
158    }
159}
160
161impl<F: FieldTrait, const PREFIX: u16> Serialize for AleoID<F, PREFIX> {
162    #[inline]
163    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
164        match serializer.is_human_readable() {
165            true => serializer.collect_str(self),
166            false => ToBytesSerializer::serialize(self, serializer),
167        }
168    }
169}
170
171impl<'de, F: FieldTrait, const PREFIX: u16> Deserialize<'de> for AleoID<F, PREFIX> {
172    #[inline]
173    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
174        match deserializer.is_human_readable() {
175            true => FromStr::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom),
176            false => FromBytesDeserializer::<Self>::deserialize(deserializer, &Self::prefix(), Self::size_in_bytes()),
177        }
178    }
179}
180
181impl<F: FieldTrait, const PREFIX: u16> Deref for AleoID<F, PREFIX> {
182    type Target = F;
183
184    #[inline]
185    fn deref(&self) -> &Self::Target {
186        &self.0
187    }
188}
189
190impl<F: FieldTrait, const PREFIX: u16> Borrow<F> for AleoID<F, PREFIX> {
191    #[inline]
192    fn borrow(&self) -> &F {
193        &self.0
194    }
195}
196
197#[allow(clippy::from_over_into)]
198impl<F: FieldTrait, const PREFIX: u16> Into<Vec<F>> for AleoID<F, PREFIX> {
199    #[inline]
200    fn into(self) -> Vec<F> {
201        vec![self.0]
202    }
203}
204
205impl<F: FieldTrait, const PREFIX: u16> Distribution<AleoID<F, PREFIX>> for StandardUniform {
206    #[inline]
207    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> AleoID<F, PREFIX> {
208        AleoID::<F, PREFIX>(Uniform::rand(rng))
209    }
210}