Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(types): Hrp wraps bech32::Hrp #1413

Merged
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
# Mandatory dependencies
bech32 = { version = "0.9.1", default-features = false }
bech32 = { version = "0.10.0-beta", default-features = false, features = [
"alloc",
] }
bitflags = { version = "2.4.0", default-features = false }
derive_more = { version = "0.99.17", default-features = false, features = [
"from",
Expand Down
94 changes: 28 additions & 66 deletions sdk/src/types/block/address/bech32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
// SPDX-License-Identifier: Apache-2.0

use alloc::{
format,
string::{String, ToString},
vec::Vec,
};
use core::str::FromStr;

use bech32::{FromBase32, ToBase32, Variant};
use derive_more::{AsRef, Deref};
use packable::{
error::{UnpackError, UnpackErrorExt},
Expand All @@ -18,67 +18,40 @@ use packable::{

use crate::types::block::{address::Address, ConvertTo, Error};

const HRP_MAX: u8 = 83;

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Hrp {
inner: [u8; HRP_MAX as usize],
len: u8,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deref)]
#[repr(transparent)]
pub struct Hrp(bech32::Hrp);

impl core::fmt::Debug for Hrp {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Hrp")
.field("display", &self.to_string())
.field("inner", &prefix_hex::encode(&self.inner[..self.len as usize]))
.field("len", &self.len)
.field("display", &self.0.to_string())
.field("bytes", &prefix_hex::encode(self.0.byte_iter().collect::<Vec<_>>()))
.field("len", &self.0.len())
kwek20 marked this conversation as resolved.
Show resolved Hide resolved
.finish()
}
}

impl Hrp {
/// Convert a string to an Hrp without checking validity.
pub const fn from_str_unchecked(hrp: &str) -> Self {
let len = hrp.len();
let mut bytes = [0; HRP_MAX as usize];
let hrp = hrp.as_bytes();
let mut i = 0;
while i < len {
bytes[i] = hrp[i];
i += 1;
}
Self {
inner: bytes,
len: len as _,
}
Self(bech32::Hrp::parse_unchecked(hrp))
}
}

impl FromStr for Hrp {
type Err = Error;

fn from_str(hrp: &str) -> Result<Self, Self::Err> {
let len = hrp.len();
if hrp.is_ascii() && len <= HRP_MAX as usize {
let mut bytes = [0; HRP_MAX as usize];
bytes[..len].copy_from_slice(hrp.as_bytes());
Ok(Self {
inner: bytes,
len: len as _,
})
} else {
Err(Error::InvalidBech32Hrp(hrp.to_string()))
}
Ok(Self(
bech32::Hrp::parse(hrp).map_err(|e| Error::InvalidBech32Hrp(format!("{hrp}: {e}")))?,
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
))
}
}

impl core::fmt::Display for Hrp {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let hrp_str = self.inner[..self.len as usize]
.iter()
.map(|b| *b as char)
.collect::<String>();
f.write_str(&hrp_str)
self.0.fmt(f)
}
}

Expand All @@ -88,8 +61,8 @@ impl Packable for Hrp {

#[inline]
fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
self.len.pack(packer)?;
packer.pack_bytes(&self.inner[..self.len as usize])?;
(self.0.len() as u8).pack(packer)?;
packer.pack_bytes(&self.0.byte_iter().collect::<Vec<_>>())?;
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved

Ok(())
}
Expand All @@ -99,21 +72,16 @@ impl Packable for Hrp {
unpacker: &mut U,
visitor: &Self::UnpackVisitor,
) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
let len = u8::unpack::<_, VERIFY>(unpacker, visitor).coerce()?;

if len > HRP_MAX {
return Err(UnpackError::Packable(Error::InvalidBech32Hrp(
"hrp len above 83".to_string(),
)));
}
let len = u8::unpack::<_, VERIFY>(unpacker, visitor).coerce()? as usize;

let mut bytes = alloc::vec![0u8; len as usize];
let mut bytes = alloc::vec![0u8; len];
unpacker.unpack_bytes(&mut bytes)?;

let mut inner = [0; 83];
inner[..len as usize].copy_from_slice(&bytes);
let hrp = bytes.into_iter().map(|b| b as char).collect::<String>();
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved

Ok(Self { inner, len })
Ok(Self(bech32::Hrp::parse(&hrp).map_err(|e| {
UnpackError::Packable(Error::InvalidBech32Hrp(e.to_string()))
})?))
}
}

Expand Down Expand Up @@ -161,14 +129,13 @@ impl FromStr for Bech32Address {
type Err = Error;

fn from_str(address: &str) -> Result<Self, Self::Err> {
match ::bech32::decode(address) {
Ok((hrp, data, _)) => {
let hrp = hrp.parse()?;
let bytes = Vec::<u8>::from_base32(&data).map_err(|_| Error::InvalidAddress)?;
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
Address::unpack_verified(bytes.as_slice(), &())
.map_err(|_| Error::InvalidAddress)
.map(|address| Self { hrp, inner: address })
}
match bech32::decode(address) {
Ok((hrp, bytes)) => Address::unpack_verified(bytes.as_slice(), &())
.map_err(|_| Error::InvalidAddress)
.map(|address| Self {
hrp: Hrp(hrp),
inner: address,
}),
Err(_) => Err(Error::InvalidAddress),
}
}
Expand Down Expand Up @@ -217,12 +184,7 @@ impl core::fmt::Display for Bech32Address {
write!(
f,
"{}",
::bech32::encode(
&self.hrp.to_string(),
self.inner.pack_to_vec().to_base32(),
Variant::Bech32
)
.unwrap()
bech32::encode::<bech32::Bech32>(self.hrp.0, &self.inner.pack_to_vec(),).unwrap()
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/tests/types/address/bech32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fn hrp_from_str() {

assert!(matches!(
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
Hrp::from_str("中國"),
Err(Error::InvalidBech32Hrp(hrp)) if hrp == "中國"
Err(Error::InvalidBech32Hrp(hrp)) if hrp.starts_with("中國")
));
}

Expand Down
Loading