Skip to main content

cml_crypto/chain_crypto/
bech32.rs

1use bech32::{Error as Bech32Error, FromBase32, ToBase32};
2use std::error::Error as StdError;
3use std::fmt;
4use std::result::Result as StdResult;
5
6pub type Result<T> = StdResult<T, Error>;
7
8pub trait Bech32 {
9    const BECH32_HRP: &'static str;
10
11    fn try_from_bech32_str(bech32_str: &str) -> Result<Self>
12    where
13        Self: Sized;
14
15    fn to_bech32_str(&self) -> String;
16}
17
18pub fn to_bech32_from_bytes<B: Bech32>(bytes: &[u8]) -> String {
19    bech32::encode(B::BECH32_HRP, bytes.to_base32())
20        .unwrap_or_else(|e| panic!("Failed to build bech32: {}", e))
21}
22
23pub fn try_from_bech32_to_bytes<B: Bech32>(bech32_str: &str) -> Result<Vec<u8>> {
24    let (hrp, bech32_data) = bech32::decode(bech32_str)?;
25    if hrp != B::BECH32_HRP {
26        return Err(Error::HrpInvalid {
27            expected: B::BECH32_HRP,
28            actual: hrp,
29        });
30    }
31    Vec::<u8>::from_base32(&bech32_data).map_err(Into::into)
32}
33
34#[derive(Debug)]
35pub enum Error {
36    Bech32Malformed(Bech32Error),
37    HrpInvalid {
38        expected: &'static str,
39        actual: String,
40    },
41    DataInvalid(Box<dyn StdError + Send + Sync + 'static>),
42}
43
44impl Error {
45    pub fn data_invalid(cause: impl StdError + Send + Sync + 'static) -> Self {
46        Error::DataInvalid(Box::new(cause))
47    }
48}
49
50impl From<Bech32Error> for Error {
51    fn from(error: Bech32Error) -> Self {
52        Error::Bech32Malformed(error)
53    }
54}
55
56impl fmt::Display for Error {
57    fn fmt(&self, f: &mut fmt::Formatter) -> StdResult<(), fmt::Error> {
58        match self {
59            Error::Bech32Malformed(_) => write!(f, "Failed to parse bech32, invalid data format"),
60            Error::HrpInvalid { expected, actual } => write!(
61                f,
62                "Parsed bech32 has invalid HRP prefix '{actual}', expected '{expected}'"
63            ),
64            Error::DataInvalid(_) => write!(f, "Failed to parse data decoded from bech32"),
65        }
66    }
67}
68
69impl StdError for Error {
70    fn source(&self) -> Option<&(dyn StdError + 'static)> {
71        match self {
72            Error::Bech32Malformed(cause) => Some(cause),
73            Error::DataInvalid(cause) => Some(&**cause),
74            _ => None,
75        }
76    }
77}