iota_stardust_types/block/address/
bech32.rs1use alloc::{
7 string::{String, ToString},
8 vec::Vec,
9};
10use core::str::FromStr;
11
12use bech32::{FromBase32, ToBase32, Variant};
13use derive_more::{AsRef, Deref};
14use packable::{
15 Packable, PackableExt,
16 error::{UnpackError, UnpackErrorExt},
17 packer::Packer,
18 unpacker::Unpacker,
19};
20
21use super::Address;
22use crate::block::Error;
23
24#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
26pub struct Hrp {
27 inner: [u8; 83],
28 len: u8,
29}
30
31impl Hrp {
32 pub const fn from_str_unchecked(hrp: &str) -> Self {
34 let len = hrp.len();
35 let mut bytes = [0; 83];
36 let hrp = hrp.as_bytes();
37 let mut i = 0;
38 while i < len {
39 bytes[i] = hrp[i];
40 i += 1;
41 }
42 Self {
43 inner: bytes,
44 len: len as _,
45 }
46 }
47
48 pub fn from_str_result(hrp: &str) -> Result<Self, Error> {
50 hrp.parse()
51 }
52}
53
54impl FromStr for Hrp {
55 type Err = Error;
56
57 fn from_str(hrp: &str) -> Result<Self, Self::Err> {
58 let len = hrp.len();
59 if hrp.is_ascii() && len <= 83 {
60 let mut bytes = [0; 83];
61 bytes[..len].copy_from_slice(hrp.as_bytes());
62 Ok(Self {
63 inner: bytes,
64 len: len as _,
65 })
66 } else {
67 Err(Error::InvalidBech32Hrp(hrp.to_string()))
68 }
69 }
70}
71
72impl core::fmt::Display for Hrp {
73 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74 let hrp_str = self.inner[..self.len as usize]
75 .iter()
76 .map(|b| *b as char)
77 .collect::<String>();
78 f.write_str(&hrp_str)
79 }
80}
81
82#[cfg(feature = "serde")]
83impl serde::Serialize for Hrp {
84 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
85 where
86 S: serde::Serializer,
87 {
88 serializer.serialize_str(&self.to_string())
89 }
90}
91
92#[cfg(feature = "serde")]
93impl<'de> serde::Deserialize<'de> for Hrp {
94 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
95 where
96 D: serde::Deserializer<'de>,
97 {
98 let s = String::deserialize(deserializer)?;
99 Self::from_str(&s).map_err(serde::de::Error::custom)
100 }
101}
102
103impl Packable for Hrp {
104 type UnpackError = Error;
105 type UnpackVisitor = ();
106
107 #[inline]
108 fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
109 self.len.pack(packer)?;
110 packer.pack_bytes(&self.inner[..self.len as usize])?;
111 Ok(())
112 }
113
114 #[inline]
115 fn unpack<U: Unpacker, const VERIFY: bool>(
116 unpacker: &mut U,
117 visitor: &Self::UnpackVisitor,
118 ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
119 let len = u8::unpack::<_, VERIFY>(unpacker, visitor).coerce()?;
120
121 if len > 83 {
122 return Err(UnpackError::Packable(Error::InvalidBech32Hrp(
123 "hrp len above 83".to_string(),
124 )));
125 }
126
127 let mut bytes = alloc::vec![0u8; len as usize];
128 unpacker.unpack_bytes(&mut bytes)?;
129
130 let mut inner = [0; 83];
131 inner[..len as usize].copy_from_slice(&bytes);
132
133 Ok(Self { inner, len })
134 }
135}
136
137impl PartialEq<String> for Hrp {
138 fn eq(&self, other: &String) -> bool {
139 self.to_string().eq(other)
140 }
141}
142
143impl PartialEq<&str> for Hrp {
144 fn eq(&self, other: &&str) -> bool {
145 self.to_string().eq(other)
146 }
147}
148
149impl PartialEq<str> for Hrp {
150 fn eq(&self, other: &str) -> bool {
151 self.to_string().eq(other)
152 }
153}
154
155#[derive(Copy, Clone, Eq, PartialEq, Hash, AsRef, Deref, Ord, PartialOrd)]
157pub struct Bech32Address {
158 pub(crate) hrp: Hrp,
159 #[as_ref]
160 #[deref]
161 pub(crate) inner: Address,
162}
163
164impl FromStr for Bech32Address {
165 type Err = Error;
166
167 fn from_str(address: &str) -> Result<Self, Self::Err> {
168 match bech32::decode(address) {
169 Ok((hrp, data, _)) => {
170 let hrp = hrp.parse()?;
171 let bytes = Vec::<u8>::from_base32(&data).map_err(|_| Error::InvalidAddress)?;
172 Address::unpack_verified(bytes.as_slice(), &())
173 .map_err(|_| Error::InvalidAddress)
174 .map(|address| Self {
175 hrp,
176 inner: address,
177 })
178 }
179 Err(_) => Err(Error::InvalidAddress),
180 }
181 }
182}
183
184impl Bech32Address {
185 pub fn new(hrp: Hrp, inner: impl Into<Address>) -> Self {
187 Self {
188 hrp,
189 inner: inner.into(),
190 }
191 }
192
193 pub fn try_new(hrp: impl AsRef<str>, inner: impl Into<Address>) -> Result<Self, Error> {
195 Ok(Self {
196 hrp: Hrp::from_str(hrp.as_ref())?,
197 inner: inner.into(),
198 })
199 }
200
201 pub fn hrp(&self) -> &Hrp {
203 &self.hrp
204 }
205
206 pub fn inner(&self) -> &Address {
208 &self.inner
209 }
210
211 pub fn into_inner(self) -> Address {
213 self.inner
214 }
215
216 pub fn try_from_str(address: impl AsRef<str>) -> Result<Self, Error> {
218 Self::from_str(address.as_ref())
219 }
220}
221
222impl core::fmt::Display for Bech32Address {
223 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
224 write!(
225 f,
226 "{}",
227 bech32::encode(
228 &self.hrp.to_string(),
229 self.inner.pack_to_vec().to_base32(),
230 Variant::Bech32
231 )
232 .unwrap()
233 )
234 }
235}
236
237impl core::fmt::Debug for Bech32Address {
238 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
239 write!(f, "Bech32Address({self})")
240 }
241}
242
243impl PartialEq<String> for Bech32Address {
244 fn eq(&self, other: &String) -> bool {
245 self.to_string().eq(other)
246 }
247}
248
249impl PartialEq<&str> for Bech32Address {
250 fn eq(&self, other: &&str) -> bool {
251 self.to_string().eq(other)
252 }
253}
254
255impl PartialEq<str> for Bech32Address {
256 fn eq(&self, other: &str) -> bool {
257 self.to_string().eq(other)
258 }
259}
260
261impl<T: core::borrow::Borrow<Bech32Address>> From<T> for Address {
262 fn from(value: T) -> Self {
263 value.borrow().inner
264 }
265}
266
267pub trait ToBech32Ext: Sized {
269 fn try_to_bech32(self, hrp: impl AsRef<str>) -> Result<Bech32Address, Error>;
272
273 fn to_bech32(self, hrp: Hrp) -> Bech32Address;
276
277 fn to_bech32_unchecked(self, hrp: impl AsRef<str>) -> Bech32Address;
280}
281
282impl<T: Into<Address>> ToBech32Ext for T {
283 fn try_to_bech32(self, hrp: impl AsRef<str>) -> Result<Bech32Address, Error> {
284 Bech32Address::try_new(hrp, self)
285 }
286
287 fn to_bech32(self, hrp: Hrp) -> Bech32Address {
288 Bech32Address::new(hrp, self)
289 }
290
291 fn to_bech32_unchecked(self, hrp: impl AsRef<str>) -> Bech32Address {
292 Bech32Address::new(Hrp::from_str_unchecked(hrp.as_ref()), self)
293 }
294}