use crate::{
constants::{
BASE58_ENCODED_32_LEN, BASE58_INVALID_CHAR, BASE58_INVERSE, BASE58_INVERSE_TABLE_OFFSET,
BASE58_INVERSE_TABLE_SENTINEL, BINARY_SZ_32, BYTE_COUNT_32, DEC_TABLE_32,
INTERMEDIATE_SZ_32, RAW58_SZ_32,
},
Error,
};
pub(crate) fn decode_32<I: AsRef<[u8]>>(input: I) -> Result<[u8; BYTE_COUNT_32], Error> {
let encoded_bytes = input.as_ref();
if encoded_bytes.len() > BASE58_ENCODED_32_LEN as usize {
return Err(Error::InputTooLong);
}
let mut char_cnt: u64 = 0;
for c in encoded_bytes.iter() {
let idx: u64 = if c < &b'1' {
u64::MAX
} else {
(*c as u64) - (BASE58_INVERSE_TABLE_OFFSET as u64)
};
let idx = std::cmp::min(idx, BASE58_INVERSE_TABLE_SENTINEL as u64);
if BASE58_INVERSE[idx as usize] == BASE58_INVALID_CHAR {
return Err(Error::InvalidCharacter);
}
char_cnt += 1;
}
let mut raw_base58: [u8; RAW58_SZ_32] = [0; RAW58_SZ_32];
let prepend_0 = RAW58_SZ_32 - char_cnt as usize;
for j in 0..RAW58_SZ_32 {
if j < prepend_0 {
raw_base58[j] = 0;
} else {
raw_base58[j] = BASE58_INVERSE
[(encoded_bytes[j - prepend_0] as usize) - BASE58_INVERSE_TABLE_OFFSET as usize];
}
}
let mut intermediate: [u64; INTERMEDIATE_SZ_32] = [0; INTERMEDIATE_SZ_32];
for i in 0..INTERMEDIATE_SZ_32 {
intermediate[i] = (raw_base58[5 * i] as u64) * 11_316_496
+ (raw_base58[5 * i + 1] as u64) * 195_112
+ (raw_base58[5 * i + 2] as u64) * 3_364
+ (raw_base58[5 * i + 3] as u64) * 58
+ (raw_base58[5 * i + 4] as u64);
}
let mut binary: [u64; BINARY_SZ_32] = [0; BINARY_SZ_32];
for j in 0..BINARY_SZ_32 {
let mut acc: u64 = 0;
for i in 0..INTERMEDIATE_SZ_32 {
acc += intermediate[i] * DEC_TABLE_32[i][j];
}
binary[j] = acc;
}
for i in (1..(BINARY_SZ_32)).rev() {
binary[i - 1] += binary[i] >> 32;
binary[i] &= 0xFFFFFFFF;
}
if binary[0] > 0xFFFFFFFF {
return Err(Error::InvalidByteAmount);
}
let mut out: [u8; BYTE_COUNT_32] = [0; BYTE_COUNT_32];
for i in 0..BINARY_SZ_32 {
let bytes = (binary[i] as u32).to_be_bytes();
out[4 * i] = bytes[0];
out[4 * i + 1] = bytes[1];
out[4 * i + 2] = bytes[2];
out[4 * i + 3] = bytes[3];
}
let mut leading_zero_cnt: usize = 0;
while leading_zero_cnt < BYTE_COUNT_32 {
if out[leading_zero_cnt] != 0 {
break;
}
if leading_zero_cnt >= encoded_bytes.len() {
return Err(Error::InputTooShort);
}
if encoded_bytes[leading_zero_cnt] != b'1' {
return Err(Error::InputTooShort);
}
leading_zero_cnt += 1;
}
if leading_zero_cnt < encoded_bytes.len() && encoded_bytes[leading_zero_cnt] == b'1' {
return Err(Error::InputTooLong);
}
Ok(out)
}
#[cfg(test)]
mod tests {
use crate::Error;
use super::decode_32;
#[test]
fn test_decode_32() {
let keys = vec![
"XkCriyrNwS3G4rzAXtG5B1nnvb5Ka1JtCku93VqeKAr",
"Awes4Tr6TX8JDzEhCZY2QVNimT6iD1zWHzf1vNyGvpLM",
"DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy",
"EgxVyTgh2Msg781wt9EsqYx4fW8wSvfFAHGLaJQjghiL",
"EvnRmnMrd69kFdbLMxWkTn1icZ7DCceRhvmb2SJXqDo4",
"Certusm1sa411sMpV9FPqU5dXAYhmmhygvxJ23S6hJ24",
"1zfbgASTPZHoQ5DhqS5f2bnJk88rxMi137DmZowDztN",
"11111111111111111111111111111111",
];
for key in keys {
let fd = decode_32(key).unwrap();
let normal = bs58::decode(key).into_vec().unwrap();
assert_eq!(fd, normal.as_slice());
}
}
#[test]
fn test_invalid_chars_32() {
let keys = vec![
"11111111111111111111111111111110",
"1111111111111111111111111111111!",
"1111111111111111111111111111111;",
"1111111111111111111111111111111I",
"1111111111111111111111111111111O",
"1111111111111111111111111111111_",
"1111111111111111111111111111111l",
];
for key in keys {
let fd = decode_32(key);
assert!(fd.is_err());
assert!(fd.is_err_and(|x| x == Error::InvalidCharacter));
}
}
#[test]
fn test_failures_32() {
let keys = vec![
"1",
"1111111111111111111111111111111",
"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJz",
"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofL",
"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofLRda4",
"111111111111111111111111111111111",
"JEKNVnkbo3jma5nREBBJCDoXFVeKkD56V3xKrvRmWxFJ",
"11aEKNVnkbo3jma5nREBBJCDoXFVeKkD56V3xKrvRmWx",
];
for key in keys {
let fd = decode_32(key);
assert!(fd.is_err());
}
}
}