snarkvm_console_network/helpers/
id.rs

1// Copyright 2024 Aleo Network Foundation
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 bech32::{self, FromBase32, ToBase32};
20use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
21use std::borrow::Borrow;
22
23pub trait Bech32ID<F: FieldTrait>:
24    From<F>
25    + Deref<Target = F>
26    + Into<Vec<F>>
27    + Uniform
28    + Copy
29    + Clone
30    + Default
31    + Debug
32    + Display
33    + FromStr
34    + ToBytes
35    + FromBytes
36    + Serialize
37    + DeserializeOwned
38    + PartialEq
39    + Eq
40    + core::hash::Hash
41    + Sync
42    + Send
43{
44    fn prefix() -> String;
45    fn size_in_bytes() -> usize;
46    fn number_of_data_characters() -> usize;
47}
48
49#[rustfmt::skip]
50#[macro_export]
51macro_rules! const_assert {
52    ($x:expr $(,)*) => {
53        pub const ASSERT: [(); 1] = [()];
54        pub const fn bool_assert(x: bool) -> bool { x }
55        let _ = ASSERT[!bool_assert($x) as usize];
56    };
57}
58
59/// Converts a string of 2 characters into a `u16` for a human-readable prefix in Bech32.
60#[macro_export]
61macro_rules! hrp2 {
62    ( $persona: expr ) => {{
63        const_assert!($persona.len() == 2);
64        let p = $persona.as_bytes();
65        u16::from_le_bytes([p[0], p[1]])
66    }};
67}
68
69#[derive(Copy, Clone, PartialEq, Eq, Hash)]
70pub struct AleoID<F: FieldTrait, const PREFIX: u16>(F);
71
72impl<F: FieldTrait, const PREFIX: u16> Bech32ID<F> for AleoID<F, PREFIX> {
73    #[inline]
74    fn prefix() -> String {
75        String::from_utf8(PREFIX.to_le_bytes().to_vec()).expect("Failed to convert prefix to string")
76    }
77
78    #[inline]
79    fn size_in_bytes() -> usize {
80        (F::size_in_bits() + 7) / 8
81    }
82
83    #[inline]
84    fn number_of_data_characters() -> usize {
85        ((Self::size_in_bytes() * 8) + 4) / 5
86    }
87}
88
89impl<F: FieldTrait, const PREFIX: u16> From<F> for AleoID<F, PREFIX> {
90    #[inline]
91    fn from(data: F) -> Self {
92        Self(data)
93    }
94}
95
96impl<F: FieldTrait, const PREFIX: u16> Default for AleoID<F, PREFIX> {
97    #[inline]
98    fn default() -> Self {
99        Self(F::zero())
100    }
101}
102
103impl<F: FieldTrait, const PREFIX: u16> FromBytes for AleoID<F, PREFIX> {
104    /// Reads data into a buffer.
105    #[inline]
106    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
107        Ok(Self(F::read_le(&mut reader)?))
108    }
109}
110
111impl<F: FieldTrait, const PREFIX: u16> ToBytes for AleoID<F, PREFIX> {
112    /// Writes the data to a buffer.
113    #[inline]
114    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
115        self.0.write_le(&mut writer)
116    }
117}
118
119impl<F: FieldTrait, const PREFIX: u16> FromStr for AleoID<F, PREFIX> {
120    type Err = Error;
121
122    /// Reads in a bech32m string.
123    #[inline]
124    fn from_str(string: &str) -> Result<Self, Self::Err> {
125        const CHECKSUM_STRING_LENGTH: usize = 6;
126        if string.len() != 3 + Self::number_of_data_characters() + CHECKSUM_STRING_LENGTH {
127            bail!("Invalid byte size for a bech32m hash: {} bytes", string.len())
128        }
129
130        let (hrp, data, variant) = bech32::decode(string)?;
131        if hrp.as_bytes() != PREFIX.to_le_bytes() {
132            bail!("Invalid prefix for a bech32m hash: {hrp}")
133        };
134        if data.is_empty() {
135            bail!("Bech32m hash data is empty")
136        }
137        if variant != bech32::Variant::Bech32m {
138            bail!("Hash is not a bech32m hash")
139        }
140        Ok(Self::read_le(&*Vec::from_base32(&data)?)?)
141    }
142}
143
144impl<F: FieldTrait, const PREFIX: u16> Display for AleoID<F, PREFIX> {
145    #[inline]
146    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
147        bech32::encode_to_fmt(
148            f,
149            &Self::prefix(),
150            self.0.to_bytes_le().expect("Failed to write data as bytes").to_base32(),
151            bech32::Variant::Bech32m,
152        )
153        .expect("Failed to encode in bech32m")
154    }
155}
156
157impl<F: FieldTrait, const PREFIX: u16> Debug for AleoID<F, PREFIX> {
158    #[inline]
159    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
160        write!(f, "AleoLocator {{ hrp: {:?}, data: {:?} }}", &Self::prefix(), self.0)
161    }
162}
163
164impl<F: FieldTrait, const PREFIX: u16> Serialize for AleoID<F, PREFIX> {
165    #[inline]
166    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
167        match serializer.is_human_readable() {
168            true => serializer.collect_str(self),
169            false => ToBytesSerializer::serialize(self, serializer),
170        }
171    }
172}
173
174impl<'de, F: FieldTrait, const PREFIX: u16> Deserialize<'de> for AleoID<F, PREFIX> {
175    #[inline]
176    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
177        match deserializer.is_human_readable() {
178            true => FromStr::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom),
179            false => FromBytesDeserializer::<Self>::deserialize(deserializer, &Self::prefix(), Self::size_in_bytes()),
180        }
181    }
182}
183
184impl<F: FieldTrait, const PREFIX: u16> Deref for AleoID<F, PREFIX> {
185    type Target = F;
186
187    #[inline]
188    fn deref(&self) -> &Self::Target {
189        &self.0
190    }
191}
192
193impl<F: FieldTrait, const PREFIX: u16> Borrow<F> for AleoID<F, PREFIX> {
194    #[inline]
195    fn borrow(&self) -> &F {
196        &self.0
197    }
198}
199
200#[allow(clippy::from_over_into)]
201impl<F: FieldTrait, const PREFIX: u16> Into<Vec<F>> for AleoID<F, PREFIX> {
202    #[inline]
203    fn into(self) -> Vec<F> {
204        vec![self.0]
205    }
206}
207
208impl<F: FieldTrait, const PREFIX: u16> Distribution<AleoID<F, PREFIX>> for Standard {
209    #[inline]
210    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> AleoID<F, PREFIX> {
211        AleoID::<F, PREFIX>(Uniform::rand(rng))
212    }
213}