Skip to content

Commit

Permalink
Merge branch '2.0' into feat/transaction-capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Coats committed Oct 17, 2023
2 parents 10ac6db + 2b83eb9 commit 7e8ec0e
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 77 deletions.
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
93 changes: 26 additions & 67 deletions sdk/src/types/block/address/bech32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use alloc::{
};
use core::str::FromStr;

use bech32::{FromBase32, ToBase32, Variant};
use derive_more::{AsRef, Deref};
use packable::{
error::{UnpackError, UnpackErrorExt},
Expand All @@ -18,67 +17,38 @@ 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())
.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)?))
}
}

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 +58,9 @@ 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)?;
// TODO revisit when/if bech32 adds a way to get the bytes without iteration to avoid collecting
packer.pack_bytes(&self.0.byte_iter().collect::<Vec<_>>())?;

Ok(())
}
Expand All @@ -99,21 +70,15 @@ 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);

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

Expand Down Expand Up @@ -161,14 +126,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)?;
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 +181,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
11 changes: 9 additions & 2 deletions sdk/src/types/block/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use alloc::string::{FromUtf8Error, String};
use core::{convert::Infallible, fmt};

use bech32::primitives::hrp::Error as Bech32HrpError;
use crypto::Error as CryptoError;
use prefix_hex::Error as HexError;
use primitive_types::U256;
Expand Down Expand Up @@ -81,7 +82,7 @@ pub enum Error {
InvalidInputKind(u8),
InvalidInputCount(<InputCount as TryFrom<usize>>::Error),
InvalidInputOutputIndex(<OutputIndex as TryFrom<u16>>::Error),
InvalidBech32Hrp(String),
InvalidBech32Hrp(Bech32HrpError),
InvalidCapabilitiesCount(<u8 as TryFrom<usize>>::Error),
InvalidBlockWrapperLength(usize),
InvalidStateMetadataLength(<StateMetadataLength as TryFrom<usize>>::Error),
Expand Down Expand Up @@ -220,7 +221,7 @@ impl fmt::Display for Error {
Self::InvalidAddress => write!(f, "invalid address provided"),
Self::InvalidAddressKind(k) => write!(f, "invalid address kind: {k}"),
Self::InvalidAccountIndex(index) => write!(f, "invalid account index: {index}"),
Self::InvalidBech32Hrp(err) => write!(f, "invalid bech32 hrp: {err}"),
Self::InvalidBech32Hrp(e) => write!(f, "invalid bech32 hrp: {e}"),
Self::InvalidCapabilitiesCount(e) => write!(f, "invalid capabilities count: {e}"),
Self::InvalidBlockKind(k) => write!(f, "invalid block kind: {k}"),
Self::InvalidRewardInputIndex(idx) => write!(f, "invalid reward input index: {idx}"),
Expand Down Expand Up @@ -390,6 +391,12 @@ impl fmt::Display for Error {
}
}

impl From<Bech32HrpError> for Error {
fn from(error: Bech32HrpError) -> Self {
Self::InvalidBech32Hrp(error)
}
}

impl From<CryptoError> for Error {
fn from(error: CryptoError) -> Self {
Self::Crypto(error)
Expand Down
12 changes: 8 additions & 4 deletions sdk/tests/types/address/bech32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@ fn ctors() {
fn hrp_from_str() {
Hrp::from_str("rms").unwrap();

assert!(matches!(
Hrp::from_str("中國"),
Err(Error::InvalidBech32Hrp(hrp)) if hrp == "中國"
));
assert!(matches!(Hrp::from_str("中國"), Err(Error::InvalidBech32Hrp(_))));
}

#[test]
Expand All @@ -61,6 +58,13 @@ fn hrp_pack_unpack() {
assert_eq!(hrp, Hrp::unpack_verified(packed_hrp.as_slice(), &()).unwrap());
}

#[test]
fn invalid_hrp_unpack() {
let packed_hrp = vec![32, 32, 32]; // invalid HRP: " "

assert!(Hrp::unpack_verified(packed_hrp.as_slice(), &()).is_err());
}

#[test]
fn bech32_into_inner() {
let address = Address::try_from_bech32(ED25519_BECH32).unwrap();
Expand Down

0 comments on commit 7e8ec0e

Please sign in to comment.