extern crate mazze_types;
#[macro_use]
extern crate lazy_static;
extern crate rustc_hex;
#[allow(dead_code)]
pub mod checksum;
pub mod consts;
pub mod errors;
#[cfg(test)]
mod tests;
use mazze_types::Address;
use checksum::polymod;
pub use consts::{AddressType, Network};
pub use errors::DecodingError;
use errors::*;
const BASE32_CHARS: &str = "abcdefghijklmnopqrstuvwxyz0123456789";
const EXCLUDE_CHARS: [char; 4] = ['o', 'i', 'l', 'q'];
lazy_static! {
static ref REGEXP: String = format!{"(?i)[:=_-{}]*", BASE32_CHARS};
static ref CHARSET: Vec<u8> =
BASE32_CHARS.replace(&EXCLUDE_CHARS[..], "").into_bytes();
static ref CHAR_INDEX: [Option<u8>; 128] = (|| {
let mut index = [None; 128];
assert_eq!(CHARSET.len(), consts::CHARSET_SIZE);
for i in 0..consts::CHARSET_SIZE {
let c = CHARSET[i] as usize;
index[c] = Some(i as u8);
let u = (c as u8 as char).to_ascii_uppercase() as u8 as usize;
if u != c {
index[u] = Some(i as u8);
}
}
return index;
}) ();
}
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct DecodedRawAddress {
pub input_base32_address: String,
pub parsed_address_bytes: Vec<u8>,
pub hex_address: Option<Address>,
pub network: Network,
}
#[derive(Copy, Clone)]
pub enum EncodingOptions {
Simple,
QrCode,
}
pub fn mazze_addr_encode(
raw: &[u8], network: Network, encoding_options: EncodingOptions,
) -> Result<String, EncodingError> {
let length = raw.len();
let version_byte = match length {
20 => consts::SIZE_160,
24 => consts::SIZE_192,
28 => consts::SIZE_224,
32 => consts::SIZE_256,
40 => consts::SIZE_320,
48 => consts::SIZE_384,
56 => consts::SIZE_448,
64 => consts::SIZE_512,
_ => return Err(EncodingError::InvalidLength(length)),
};
let prefix = network.to_prefix()?;
let mut payload = Vec::with_capacity(1 + raw.len());
payload.push(version_byte);
payload.extend(raw);
let payload_5_bits = convert_bits(&payload, 8, 5, true)
.expect("no error is possible for encoding");
let payload_str: String = payload_5_bits
.iter()
.map(|b| CHARSET[*b as usize] as char)
.collect();
let expanded_prefix = expand_prefix(&prefix);
let checksum_input =
[&expanded_prefix[..], &payload_5_bits, &[0; 8][..]].concat();
let checksum = polymod(&checksum_input);
let checksum_str: String = (0..8)
.rev()
.map(|i| CHARSET[((checksum >> (i * 5)) & 31) as usize] as char)
.collect();
let mazze_base32_addr = match encoding_options {
EncodingOptions::Simple => {
[&prefix, ":", &payload_str, &checksum_str].concat()
}
EncodingOptions::QrCode => {
let addr_type_str = AddressType::from_address(&raw)?.to_str();
[
&prefix,
":type.",
addr_type_str,
":",
&payload_str,
&checksum_str,
]
.concat()
.to_uppercase()
}
};
Ok(mazze_base32_addr)
}
pub fn mazze_addr_decode(
addr_str: &str,
) -> Result<DecodedRawAddress, DecodingError> {
let has_lowercase = addr_str.chars().any(|c| c.is_lowercase());
let has_uppercase = addr_str.chars().any(|c| c.is_uppercase());
if has_lowercase && has_uppercase {
return Err(DecodingError::MixedCase);
}
let lowercase = addr_str.to_lowercase();
let parts: Vec<&str> = lowercase.split(':').collect();
if parts.len() < 2 {
return Err(DecodingError::NoPrefix);
}
let prefix = parts[0];
let network = Network::from_prefix(prefix)?;
let mut address_type = None;
for option_str in &parts[1..parts.len() - 1] {
let key_value: Vec<&str> = option_str.split('.').collect();
if key_value.len() != 2 {
return Err(DecodingError::InvalidOption(OptionError::ParseError(
(*option_str).into(),
)));
}
if key_value[0] == "type" {
address_type = Some(AddressType::parse(key_value[1])?);
}
}
let payload_str = parts[parts.len() - 1];
if payload_str.len() == 0 {
return Err(DecodingError::InvalidLength(0));
}
let has_lowercase = payload_str.chars().any(|c| c.is_lowercase());
let has_uppercase = payload_str.chars().any(|c| c.is_uppercase());
if has_lowercase && has_uppercase {
return Err(DecodingError::MixedCase);
}
let payload_chars = payload_str.chars();
let payload_5_bits: Result<Vec<u8>, DecodingError> = payload_chars
.map(|c| {
let i = c as usize;
if let Some(Some(d)) = CHAR_INDEX.get(i) {
Ok(*d as u8)
} else {
Err(DecodingError::InvalidChar(c))
}
})
.collect();
let payload_5_bits = payload_5_bits?;
let checksum =
polymod(&[&expand_prefix(prefix), &payload_5_bits[..]].concat());
if checksum != 0 {
return Err(DecodingError::ChecksumFailed(checksum));
}
let len_5_bit = payload_5_bits.len();
let payload =
convert_bits(&payload_5_bits[..(len_5_bit - 8)], 5, 8, false)?;
let version = payload[0];
let body = &payload[1..];
let body_len = body.len();
let version_size = version & consts::SIZE_MASK;
if (version_size == consts::SIZE_160 && body_len != 20)
|| (version_size == consts::SIZE_192 && body_len != 24)
|| (version_size == consts::SIZE_224 && body_len != 28)
|| (version_size == consts::SIZE_256 && body_len != 32)
|| (version_size == consts::SIZE_320 && body_len != 40)
|| (version_size == consts::SIZE_384 && body_len != 48)
|| (version_size == consts::SIZE_448 && body_len != 56)
|| (version_size == consts::SIZE_512 && body_len != 64)
{
return Err(DecodingError::InvalidLength(body_len));
}
if version & consts::RESERVED_BITS_MASK != 0 {
return Err(DecodingError::VersionNotRecognized(version));
}
let hex_address;
if version_size == consts::SIZE_160 {
hex_address = Some(Address::from_slice(body));
match address_type {
Some(expected) => {
let got =
AddressType::from_address(hex_address.as_ref().unwrap())
.or(Err(()));
if got.as_ref() != Ok(&expected) {
return Err(DecodingError::InvalidOption(
OptionError::AddressTypeMismatch { expected, got },
));
}
}
None => {}
}
} else {
hex_address = None;
}
Ok(DecodedRawAddress {
input_base32_address: addr_str.into(),
parsed_address_bytes: body.to_vec(),
hex_address,
network,
})
}
fn expand_prefix(prefix: &str) -> Vec<u8> {
let mut ret: Vec<u8> = prefix.chars().map(|c| (c as u8) & 0x1f).collect();
ret.push(0);
ret
}
fn convert_bits(
data: &[u8], inbits: u8, outbits: u8, pad: bool,
) -> Result<Vec<u8>, DecodingError> {
assert!(inbits <= 8 && outbits <= 8);
let num_bytes = (data.len() * inbits as usize + outbits as usize - 1)
/ outbits as usize;
let mut ret = Vec::with_capacity(num_bytes);
let mut acc: u16 = 0; let mut num: u8 = 0; let groupmask = (1 << outbits) - 1;
for d in data.iter() {
acc = (acc << inbits) | u16::from(*d);
num += inbits;
while num >= outbits {
ret.push((acc >> (num - outbits)) as u8);
acc &= !(groupmask << (num - outbits));
num -= outbits;
}
}
if pad {
if num > 0 {
ret.push((acc << (outbits - num)) as u8);
}
} else {
let padding = ((data.len() * inbits as usize) % outbits as usize) as u8;
if num >= inbits || acc != 0 {
return Err(DecodingError::InvalidPadding {
from_bits: inbits,
padding_bits: padding,
padding: acc,
});
}
}
Ok(ret)
}
#[test]
fn test_expand_prefix() {
assert_eq!(expand_prefix("mazze"), vec![0x03, 0x06, 0x18, 0x00]);
assert_eq!(
expand_prefix("mazzetest"),
vec![0x03, 0x06, 0x18, 0x14, 0x05, 0x13, 0x14, 0x00]
);
assert_eq!(
expand_prefix("net17"),
vec![0x0e, 0x05, 0x14, 0x11, 0x17, 0x00]
);
}
#[test]
fn test_convert_bits() {
assert_eq!(convert_bits(&[0], 8, 1, false), Ok(vec![0; 8]));
assert_eq!(convert_bits(&[0], 8, 3, false), Ok(vec![0, 0])); assert_eq!(convert_bits(&[0], 8, 3, true), Ok(vec![0, 0, 0]));
assert!(convert_bits(&[1], 8, 3, false).is_err()); assert_eq!(convert_bits(&[1], 8, 3, true), Ok(vec![0, 0, 2]));
assert_eq!(convert_bits(&[1], 8, 7, true), Ok(vec![0, 64]));
assert_eq!(convert_bits(&[0; 8], 1, 8, false), Ok(vec![0]));
assert_eq!(convert_bits(&[0, 0, 2], 3, 8, false), Ok(vec![1])); assert_eq!(convert_bits(&[0, 0, 2], 3, 8, true), Ok(vec![1, 0]));
assert!(convert_bits(&[0, 0, 3], 3, 8, false).is_err());
assert_eq!(
convert_bits(&[0, 1, 2, 3, 4], 8, 5, false),
Ok(vec![0, 0, 0, 16, 4, 0, 24, 4])
);
assert!(convert_bits(&[0, 1, 2], 8, 5, false).is_err());
assert_eq!(
convert_bits(&[0, 1, 2], 8, 5, true),
Ok(vec![0, 0, 0, 16, 4])
);
assert_eq!(
convert_bits(&[0, 0, 0, 16, 4, 0, 24, 4], 5, 8, false),
Ok(vec![0, 1, 2, 3, 4])
);
assert_eq!(
convert_bits(&[0, 0, 0, 16, 4], 5, 8, false),
Ok(vec![0, 1, 2])
);
assert_eq!(
convert_bits(&[0, 0, 0, 16, 4], 5, 8, true),
Ok(vec![0, 1, 2, 0])
); }