#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#![cfg_attr(bench, feature(test))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![deny(missing_docs)]
#[cfg(bench)]
extern crate test;
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(any(test, feature = "std"))]
extern crate core;
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
use alloc::{string::String, vec::Vec};
use core::fmt;
use crate::error::write_err;
#[doc(inline)]
pub use crate::primitives::checksum::Checksum;
#[cfg(doc)]
use crate::primitives::decode::CheckedHrpstring;
#[cfg(feature = "alloc")]
use crate::primitives::decode::UncheckedHrpstringError;
#[cfg(feature = "alloc")]
use crate::primitives::decode::{ChecksumError, UncheckedHrpstring};
#[doc(inline)]
pub use crate::primitives::gf32::Fe32;
#[doc(inline)]
pub use crate::primitives::hrp::Hrp;
#[doc(inline)]
pub use crate::primitives::iter::{ByteIterExt, Fe32IterExt};
#[doc(inline)]
pub use crate::primitives::{Bech32, Bech32m, NoChecksum};
mod error;
pub mod hrp;
pub mod primitives;
pub mod segwit;
#[cfg(feature = "alloc")]
#[inline]
pub fn decode(s: &str) -> Result<(Hrp, Vec<u8>), DecodeError> {
let unchecked = UncheckedHrpstring::new(s)?;
if let Err(e) = unchecked.validate_checksum::<Bech32m>() {
if !unchecked.has_valid_checksum::<Bech32>() {
return Err(DecodeError::Checksum(e));
}
};
let checked = unchecked.remove_checksum::<Bech32m>();
Ok((checked.hrp(), checked.byte_iter().collect()))
}
#[cfg(feature = "alloc")]
#[inline]
pub fn encode<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error> {
encode_lower::<Ck>(hrp, data)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn encode_lower<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error> {
let mut buf = String::new();
encode_lower_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
Ok(buf)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn encode_upper<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error> {
let mut buf = String::new();
encode_upper_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
Ok(buf)
}
#[inline]
pub fn encode_to_fmt<Ck: Checksum, W: fmt::Write>(
fmt: &mut W,
hrp: Hrp,
data: &[u8],
) -> Result<(), fmt::Error> {
encode_lower_to_fmt::<Ck, W>(fmt, hrp, data)
}
#[inline]
pub fn encode_lower_to_fmt<Ck: Checksum, W: fmt::Write>(
fmt: &mut W,
hrp: Hrp,
data: &[u8],
) -> Result<(), fmt::Error> {
let iter = data.iter().copied().bytes_to_fes();
let chars = iter.with_checksum::<Ck>(&hrp).chars();
for c in chars {
fmt.write_char(c)?;
}
Ok(())
}
#[inline]
pub fn encode_upper_to_fmt<Ck: Checksum, W: fmt::Write>(
fmt: &mut W,
hrp: Hrp,
data: &[u8],
) -> Result<(), fmt::Error> {
let iter = data.iter().copied().bytes_to_fes();
let chars = iter.with_checksum::<Ck>(&hrp).chars();
for c in chars {
fmt.write_char(c.to_ascii_uppercase())?;
}
Ok(())
}
#[cfg(feature = "std")]
#[inline]
pub fn encode_to_writer<Ck: Checksum, W: std::io::Write>(
w: &mut W,
hrp: Hrp,
data: &[u8],
) -> Result<(), std::io::Error> {
encode_lower_to_writer::<Ck, W>(w, hrp, data)
}
#[cfg(feature = "std")]
#[inline]
pub fn encode_lower_to_writer<Ck: Checksum, W: std::io::Write>(
w: &mut W,
hrp: Hrp,
data: &[u8],
) -> Result<(), std::io::Error> {
let iter = data.iter().copied().bytes_to_fes();
let chars = iter.with_checksum::<Ck>(&hrp).chars();
for c in chars {
w.write_all(&[c as u8])?;
}
Ok(())
}
#[cfg(feature = "std")]
#[inline]
pub fn encode_upper_to_writer<Ck: Checksum, W: std::io::Write>(
w: &mut W,
hrp: Hrp,
data: &[u8],
) -> Result<(), std::io::Error> {
let iter = data.iter().copied().bytes_to_fes();
let chars = iter.with_checksum::<Ck>(&hrp).chars();
for c in chars {
w.write_all(&[c.to_ascii_uppercase() as u8])?;
}
Ok(())
}
#[cfg(feature = "alloc")]
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum DecodeError {
Parse(UncheckedHrpstringError),
Checksum(ChecksumError),
}
#[cfg(feature = "alloc")]
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use DecodeError::*;
match *self {
Parse(ref e) => write_err!(f, "parsing failed"; e),
Checksum(ref e) => write_err!(f, "no valid bech32 or bech32m checksum"; e),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DecodeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use DecodeError::*;
match *self {
Parse(ref e) => Some(e),
Checksum(ref e) => Some(e),
}
}
}
#[cfg(feature = "alloc")]
impl From<UncheckedHrpstringError> for DecodeError {
#[inline]
fn from(e: UncheckedHrpstringError) -> Self { Self::Parse(e) }
}
#[cfg(feature = "std")]
#[derive(Debug)]
#[non_exhaustive]
pub enum DecodeFromReaderError {
Read(std::io::Error),
Decode(DecodeError),
}
#[cfg(feature = "std")]
impl fmt::Display for DecodeFromReaderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use DecodeFromReaderError::*;
match *self {
Read(ref e) => write_err!(f, "read failed"; e),
Decode(ref e) => write_err!(f, "decoding failed"; e),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DecodeFromReaderError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use DecodeFromReaderError::*;
match *self {
Read(ref e) => Some(e),
Decode(ref e) => Some(e),
}
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for DecodeFromReaderError {
#[inline]
fn from(e: std::io::Error) -> Self { Self::Read(e) }
}
#[cfg(feature = "std")]
impl From<DecodeError> for DecodeFromReaderError {
#[inline]
fn from(e: DecodeError) -> Self { Self::Decode(e) }
}
#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
use super::*;
use crate::Bech32;
#[rustfmt::skip]
const DATA: [u8; 20] = [
0xff, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4,
0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23,
0xf1, 0x43, 0x3b, 0xd6,
];
#[test]
fn encode_bech32m() {
let hrp = Hrp::parse_unchecked("test");
let got = encode::<Bech32m>(hrp, &DATA).expect("failed to encode");
let want = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kmz4lky";
assert_eq!(got, want);
}
#[test]
fn encode_bech32_lower() {
let hrp = Hrp::parse_unchecked("test");
let got = encode_lower::<Bech32>(hrp, &DATA).expect("failed to encode");
let want = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kw79nnx";
assert_eq!(got, want);
}
#[test]
#[cfg(feature = "std")]
fn encode_bech32_lower_to_writer() {
let hrp = Hrp::parse_unchecked("test");
let mut buf = Vec::new();
encode_lower_to_writer::<Bech32, _>(&mut buf, hrp, &DATA).expect("failed to encode");
let got = std::str::from_utf8(&buf).expect("ascii is valid utf8");
let want = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kw79nnx";
assert_eq!(got, want);
}
#[test]
fn encode_bech32_upper() {
let hrp = Hrp::parse_unchecked("test");
let got = encode_upper::<Bech32>(hrp, &DATA).expect("failed to encode");
let want = "TEST1LU08D6QEJXTDG4Y5R3ZARVARY0C5XW7KW79NNX";
assert_eq!(got, want);
}
#[test]
#[cfg(feature = "std")]
fn encode_bech32_upper_to_writer() {
let hrp = Hrp::parse_unchecked("test");
let mut buf = Vec::new();
encode_upper_to_writer::<Bech32, _>(&mut buf, hrp, &DATA).expect("failed to encode");
let got = std::str::from_utf8(&buf).expect("ascii is valid utf8");
let want = "TEST1LU08D6QEJXTDG4Y5R3ZARVARY0C5XW7KW79NNX";
assert_eq!(got, want);
}
#[test]
fn decode_bech32m() {
let s = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kmz4lky";
let (hrp, data) = decode(s).expect("failed to encode");
assert_eq!(hrp, Hrp::parse_unchecked("test"));
assert_eq!(data, DATA);
}
#[test]
fn decode_bech32_lower() {
let s = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kw79nnx";
let (hrp, data) = decode(s).expect("failed to encode");
assert_eq!(hrp, Hrp::parse_unchecked("test"));
assert_eq!(data, DATA);
}
#[test]
fn decode_bech32_upper() {
let s = "TEST1LU08D6QEJXTDG4Y5R3ZARVARY0C5XW7KW79NNX";
let (hrp, data) = decode(s).expect("failed to encode");
assert_eq!(hrp, Hrp::parse_unchecked("TEST"));
assert_eq!(data, DATA);
}
}